类的六个默认成员函数
如果一个类中什么成员都没有,简称为空类。空类中什么都没有吗?并不是的,任何一个类在我们不写的情 况下,都会自动生成下面6个默认成员函数。
class Date{};
构造函数
构造函数的特性:
构造函数是特殊的成员函数,需要注意的是,构造函数的虽然名称叫构造,但是需要注意的是构造函数的主 要任务并不是开空间创建对象,而是初始化对象。
其特征如下:
函数名与类名相同。 无返回值。 对象实例化时编译器自动调用对应的构造函数。 构造函数可以重载。 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定 义编译器将不再生成。 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参 构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认成员函数 关于编译器生成的默认成员函数,很多童鞋会有疑惑:在我们不实现构造函数的情况下,编译器会生成 默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但 是d对象year/month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么卵 用?? 解答:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语法已经定义好的类型:如 int/char…,自定义类型就是我们使用class/struct/union自己定义的类型,看看下面的程序,就会发现 编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数 成员变量的命名风格
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Date {public : Date (int year) { year = year; }private : int year; };class Date {public : Date (int year) { _year = year; } private : int _year; };class Date {public : Date (int year) { m_year = year; }private : int m_year; };
其实总结一下就是容易区分变量名,不容易搞混。
日期类中的构造函数:
1 2 3 Date (int year = 1 , int month = 1 , int day = 1 );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Date::Date (int year , int month ,int day ) { if (year >= 1 && month <= 12 && month >= 1 && day >= 1 && day <= GetMonthDay (year, month)) { _year = year; _month = month; _day = day; } else { cout << "日期非法" << endl; } }
注意 当函数的声明和定义分离时,缺省值在声明给,定义不用给!!!
析构函数
概念
前面通过构造函数的学习,我们知道一个对象时怎么来的,那一个对象又是怎么没呢的? 析构函数:与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而 对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。
特性
析构函数是特殊的成员函数。 其特征如下:
1.析构函数名是在类名前加上字符 ~。
2.无参数无返回值。
3.一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
4.对象生命周期结束时,C++编译系统系统自动调用析构函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 typedef int DataType;class SeqList { public : SeqList (int capacity = 10 ) { _pData = (DataType*)malloc (capacity * sizeof (DataType)); assert (_pData); _size = 0 ; _capacity = capacity; } ~SeqList () { if (_pData) { free (_pData ); _pData = NULL ; _capacity = 0 ; _size = 0 ; } } private : int * _pData ; size_t _size; size_t _capacity; };
5.关于编译器自动生成的析构函数,是否会完成一些事情呢?下面的程序我们会看到,编译器生成的默认 析构函数, 对会自定类型成员调用它的析构函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class String {public : String (const char * str = "jack" ) { _str = (char *)malloc (strlen (str) + 1 ); strcpy (_str, str); } ~String () { cout << "~String()" << endl; free (_str); }private : char * _str; };class Person { private : String _name; int _age; };int main () { Person p; return 0 ; }
拷贝构造函数
拷贝构造函数也是特殊的成员函数,其特征如下:
拷贝构造函数是构造函数的一个重载形式。 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用。
1 2 3 4 Date (const Date& d);
1 2 3 4 5 6 7 Date::Date (const Date& d) { _year = d._year; _month = d._month; _day = d._day; }
3.若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷 贝,这种拷 贝我们叫做浅拷贝,或者值拷贝。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Date {public : Date (int year = 1900 , int month = 1 , int day = 1 ) { _year = year; _month = month; _day = day; }private : int _year; int _month; int _day; };int main () { Date d1; Date d2 (d1) ; return 0 ; }
4.那么编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,我们还需要自己实现吗?当然像 日期类这样 的类是没必要的。那么下面的类呢?验证一下试试?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class String {public : String (const char * str = "jack" ) { _str = (char *)malloc (strlen (str) + 1 ); strcpy (_str, str); } ~String () { cout << "~String()" << endl; free (_str); }private : char * _str; };int main () { String s1 ("hello" ) ; String s2 (s1) ; }
操作符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类 型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
注意: 不能通过连接其他符号来创建新的操作符:比如operator@
重载操作符必须有一个类类型或者枚举类型的操作数
用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义
作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的 操作符有一个默认的形参this,限定 为第一个形参 .
.*、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
+=重载
改变自己的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Date& operator +=(int day); Date& Date:: operator +=(int day) { if (day < 0 ) { return (*this ) -= (-day); } _day += day; while (_day>GetMonthDay (_year,_month)) { _day -= GetMonthDay (_year, _month);; ++_month; if (_month ==13 ) { _year++; _month = 1 ; } } return *this ; }
+操作符重载
不改变自己的值
写+= 然后+复用+= 因为+=是引用返回,效率能高一点
1 2 3 4 5 6 7 8 9 Date operator +(int day)const ; Date Date:: operator +(int day)const { Date d (*this ) ; d += day; return d; }
-=重载
改变自己的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Date& operator -=(int day); Date& Date:: operator -=(int day) { if (day < 0 ) { return (*this ) +=(-day); } _day -= day; while (_day<GetMonthDay (_year,_month)) { --_month; if (_month == 0 ) { _year--; _month = 12 ; } _day += GetMonthDay (_year, _month); } return *this ; }
-操作符重载
不改变自己的值
1 2 3 4 5 6 7 8 9 Date operator -(int day)const ; Date Date:: operator -(int day)const { Date d (*this ) ; d -= day; return d; }
前置++重载
先加再用
1 2 3 4 5 6 Date& Date:: operator ++() { *this += 1 ; return *this ; }
后置++重载
先用再加
1 2 3 4 5 6 7 8 Date Date:: operator ++(int ) { Date tmp (*this ) ; *this += 1 ; return tmp; }
前置 - -重载
先减再用
1 2 3 4 5 6 Date& Date:: operator --() { *this -= 1 ; return *this ; }
后置- - 重载
先用再减
1 2 3 4 5 6 7 Date Date:: operator --(int ) { Date tmp (*this ) ; *this -= 1 ; return tmp; }
比较运算符重载只需要写一个等于和一个小于(大于和等于也可以),其余的复用就行了,看我代码就可以理解
>重载
1 2 3 4 5 bool operator >(const Date& d)const { return !(*this <= d); }
<重载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 bool Date:: operator < (const Date& d)const { if ((_year < d._year) || (_year == d._year && _month < d._month) || (_year == d._year && _month == d._month && _day < d._day)) { return true ; } else { return false ; } }
>=重载
1 2 3 4 5 inline bool operator >= (const Date& d)const { return !(*this < d); }
<=重载
1 2 3 4 5 bool operator <= (const Date& d)const { return ((*this < d) || (*this == d)); }
==重载
1 2 3 4 5 6 7 bool Date:: operator ==(const Date& d) const { return ((_year == d._year) && (_month == d._month) && (_day == d._day)); }
!=重载
1 2 3 4 5 bool operator != (const Date& d)const { return !(*this == d); }
赋值运算符 = 重载
1 2 3 4 5 6 7 8 9 10 11 Date& Date::operator =(const Date& d) { if (this != &d) { _year = d._year; _month = d._month; _day = d._day; } return *this ; }
const成员函数
在不改变类成员变量的情况下我们一般给类的成员函数加const
可由于this指针是隐藏的,所以我们在函数名后面加const
就像这样:void Print()const;
日期类我给能加const的函数都加了
取地址及const取地址操作符重载
这两个默认成员函数一般不用重新定义 ,编译器默认会生成。
这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比 如不想让别人获取到指定的内容!
1 2 3 4 5 6 7 8 9 10 11 12 Date* Date:: operator &() { return nullptr ; }const Date* Date:: operator &()const { return nullptr ; }
日期类的实现
Date.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 #pragma once #include <iostream> using namespace std;class Date {public : int GetMonthDay (int year, int month) ; Date (int year = 1 , int month = 1 , int day = 1 ); void Print () const ; Date (const Date& d); Date& operator =(const Date& d); Date& operator +=(int day); Date operator +(int day)const ; Date operator -(int day)const ; Date& operator -=(int day); Date& operator ++(); Date operator ++(int ); Date operator --(int ); Date& operator --(); bool operator >(const Date& d)const { return !(*this <= d); } bool operator ==(const Date& d) const ; inline bool operator >= (const Date& d)const { return !(*this < d); } bool operator < (const Date& d)const ; bool operator <= (const Date& d)const { return ((*this < d) || (*this == d)); } bool operator != (const Date& d)const { return !(*this == d); } int operator -(const Date& d)const ;private : int _year; int _month; int _day; };
Date.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 #define _CRT_SECURE_NO_WARNINGS 1 #include "date.h" int Date:: GetMonthDay (int year, int month) { static int Mday[13 ] = { 0 ,31 ,28 ,31 ,30 ,31 ,30 ,31 ,31 ,30 ,31 ,30 ,31 }; if (((year % 4 == 0 && year % 100 != 0 ) || (year % 400 == 0 )) && (month == 2 )) { return 29 ; } else { return Mday[month]; } } Date::Date (int year , int month ,int day ) { if (year >= 1 && month <= 12 && month >= 1 && day >= 1 && day <= GetMonthDay (year, month)) { _year = year; _month = month; _day = day; } else { cout << "日期非法" << endl; } }void Date::Print () const { cout << _year << "-" << _month << "-" << _day << endl; } Date::Date (const Date& d) { _year = d._year; _month = d._month; _day = d._day; } Date& Date::operator =(const Date& d) { if (this != &d) { _year = d._year; _month = d._month; _day = d._day; } return *this ; } Date& Date:: operator +=(int day) { if (day < 0 ) { return (*this ) -= (-day); } _day += day; while (_day>GetMonthDay (_year,_month)) { _day -= GetMonthDay (_year, _month);; ++_month; if (_month ==13 ) { _year++; _month = 1 ; } } return *this ; } Date Date:: operator +(int day)const { Date d (*this ) ; d += day; return d; } Date Date:: operator -(int day)const { Date d (*this ) ; d -= day; return d; } Date& Date:: operator -=(int day) { if (day < 0 ) { return (*this ) +=(-day); } _day -= day; while (_day<GetMonthDay (_year,_month)) { --_month; if (_month == 0 ) { _year--; _month = 12 ; } _day += GetMonthDay (_year, _month); } return *this ; } Date& Date:: operator ++() { *this += 1 ; return *this ; } Date Date:: operator ++(int ) { Date tmp (*this ) ; *this += 1 ; return tmp; } Date Date:: operator --(int ) { Date tmp (*this ) ; *this -= 1 ; return tmp; } Date& Date:: operator --() { *this -= 1 ; return *this ; }bool Date:: operator ==(const Date& d) const { return ((_year == d._year) && (_month == d._month) && (_day == d._day)); }bool Date:: operator < (const Date& d)const { if ((_year < d._year) || (_year == d._year && _month < d._month) || (_year == d._year && _month == d._month && _day < d._day)) { return true ; } else { return false ; } }int Date:: operator -(const Date& d)const { int flag = 1 ; int count = 0 ; Date max = *this ; Date min = d; if ((*this )< d) { flag = -1 ; min = *this ; max = d; } while (min!=max) { min++; count++; } return count * flag; }