类的默认成员函数
默认函数就是程序员没有写,编译器会自动生成的函数。默认成员函数可以大致分为以下几类:
在C++11标准之后还会增加两个默认成员函数,移动构造和移动赋值
构造函数
构造函数是特殊的成员函数,虽然名字叫构造,但是构造函数的作用不是为对象开辟空间(我们常使用的局部对象是栈帧创建时,空间就开好了),而是初始化对象。类似用C语言实现数据结构Stack时的Init。
特点:
函数名与类名相同
无返回值(不需要加void)
实例化对象时自动调用对应的构造函数
可以重载
不写的话编译器会自己默认生成一个无参的构造函数,这个构造函数对内置类型怎么处理C++标准中没有规定,有些编译器什么都不干,有些编译器可能会把内置类型初始化为0,但是我们一般认为编译器自己生成的构造函数对内置类型什么都不干,而对于自定义类型,编译器会调用自定义类型的默认构造函数(没有的话会报错)
默认构造函数是指全缺省的构造函数,没有参数的构造函数,以及编译器自动生成的构造函数,这三个在一个类中只能存在一个
总结:这个构造函数一般都要自己写,除了两个队列实现一个栈(类里面只有自定义类型)的特殊情况,不自己写的话编译器自动生成的满足不了我们的需求
析构函数
析构函数完成的是对对象中资源的清理释放,就像Stack中的destroy函数。
特点:
析构函数名是在类名前面加上字符~
无参数,无返回值(同样不需要加void)
一个类只能有一个析构函数
对象生命周期结束时自动调用
编译器默认生成的析构函数对内置类型不做处理,对自定义类型会调用自定义类型的析构函数
程序员显式写的析构函数,即使不调用自定义类型的析构函数,编译器也会自动调用自定义类型的析构函数(真就不得不佩服创造C++这门语言的人了,弟子作死祖师爷也能把弟子就回来)
如果对象中有资源申请,那么一定要自己写析构,否则就会造成内存泄漏
一个局部域的多个对象,C++规定后定义的先析构
总结:有资源申请的才要写,没有资源申请的不用管,编译器自己生成的够用了
拷贝构造函数
拷贝构造函数指的是一个第一个参数是自身对象的引用,没有其他参数或者其他参数都有默认值的构造函数(一般都没有其他参数)
特点:
拷贝构造函数是构造函数的一个重载
拷贝构造函数的第一个参数必须是当前类类型对象的引用,如果使用传值方式,则编译器会报错,因为会引发无穷递归错误
C++规定自定义类型对象进行拷贝行为都要调用拷贝构造函数,所以自定义类型的传值返回和传值使用都通过调用拷贝构造完成
若没有写拷贝构造函数,编译器会自己生成一个拷贝构造函数,这个拷贝构造函数对内置类型进行浅拷贝(一个一个字节进行拷贝),对自定义类型会调用它的拷贝构造
类成员变量没有指向资源的,用编译器自动生成的拷贝构造就可以了;类成员变量指向资源的,由于浅拷贝无法完成任务,要自己写可以完成深拷贝的拷贝构造;对于两个栈实现一个队列的队列类,不用写,因为编译器自动生成的会调用Stack中的拷贝构造
传值返回会产生一个临时对象调用拷贝构造,传值引用返回返回的是别名,没有调用拷贝构造,因此传值引用返回更加高效,但是如果返回的对象是在函数内部创建的局部对象,就只能使用传值返回,传值引用返回会造成野引用(要确保函数结束后对象还在才能传值引用返回)
两种使用拷贝构造的方式:
//假设d2是以存在的对象
Date d1(d2);
Date d1 = d2;
//显然使用第二种更爽
总结:需要深拷贝才写,浅拷贝用自动生成的就好了
赋值运算符重载
运算符重载
当运算符用于类类型对象时,C++允许我们通过运算符重载为运算符指定新的含义。如果未进行运算符重载的运算符用于类类型对象,则编译会报错
运算符重载是具有特殊名字的函数,其名字是有operator加上后面的运算符共同组成,与其他函数一样,也具有返回值,参数列表以及函数体
重载运算符的参数对象和运算符作用的运算对象一样多,一元一个,二元两个
如果一个重载运算符是成员函数,那么第一个参数是隐式的this指针,因此我们看到的参数比实际参数少一个
不能创造新的操作符,比如:
operater@
语法中没有@这个操作符
.* :: ?: sizeof . 不能重载
重载操作符必须至少有一个类类型的参数
重载后要有意义,比如日期加日期无意义,日期减日期才有意义
重载++运算符时,有前置++和后置++,名字都是operator++,无法区分,于是C++标准中规定,后置++要多一个形参int,--同理
重载>>和<<时,不能重置为成员函数,只能重置为全局函数,因为如果重载this指针会默认抢占第一个参数的位置,不符合使用习惯
赋值运算符重载
赋值运算符只能作为当前类的成员函数重载,这是规定,赋值运算符的重载的参数建议写成当前类const的当前类对象的引用(不传引用也行,但是会有一次拷贝,这样会降低效率)
有返回值,建议写成当前类对象的引用,有返回值是为了支持连续赋值
我们没有写的时候,编译器会自己默认生成一个,这个默认生成的赋值运算符重载和拷贝构造函数类似,都会完成对内置类型的浅拷贝,对自定义类型则调用它的赋值运算符重载
成员变量不指向资源的情况下,默认生成的就可以用了;成员变量指向资源的情况下,要自己手写,否则就会造成两个对象中的成员变量指向同一块资源
取地址运算符重载
const成员函数
将const修饰的成员函数称为const成员函数,const成员函数中的const放到参数列表后面
const修饰的是参数中隐含的this指针,表示this指针指向的内容不能改,一般不该内容的成员函数都建议写成const成员函数