- 1、拷贝构造函数
- 2、赋值运算符重载
- 3、const成员
什么是拷贝构造函数?
构造函数只有单个形参,该形参是对本类类型对象的引用(一般用const修饰),在用已存在的类类型创建新对象时由编译器自动调用。
拷贝构造函数的特点
1.拷贝构造函数时构造函数的一个重载形式
2.拷贝构造函数的参数只有一个且必须使用引用传参,使用传值会引发无穷递归调用
3.若未显示定义,系统生成默认的拷贝构造函数。
对内置类型成员,默认的拷贝构造函数会完成按字节序的拷贝,这种拷贝我们叫做浅拷贝或值拷贝。
对于自定义类型,会调用它的默认拷贝构造函数。
4.编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,我们还需要自己实现吗?
#define _CRT_SECURE_NO_WARNINGS 1 #include#include using namespace std; class String { private: char* _str; public: String(const char* str = "jack") { _str = (char*)malloc(strlen(str) + 1); strcpy(_str, str); } ~String() { cout << "~string()" << endl; free(_str); } }; int main() { String s1("hello"); String s2(s1); }
以上程序会崩溃。s2为s1的复制,会调用默认的拷贝构造函数。默认的拷贝构造函数只是简单的把两个对象的指针做赋值运算,他们指向的是同一块地址。但是产生两次析构,释放同一块内存而引发程序崩溃。因此需要我们自己实现深拷贝的拷贝构造函数。
2、赋值运算符重载什么是运算符重载
C++为了增强代码的可读性引入运算符重载,运算符重载是具有特殊函数名的函数,也具有返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通函数类似。
函数名:关键字operator后边接需要重载的运算符符号
函数原型:返回值类型operator操作符(参数列表)
注意:
不能通过其他连接符号来创建新的操作符:比如operator@
重载操作符必须有一个类类型或者枚举类型的操作数
用于内置类型的操作数,其含义不能改变,例如:内置的整型+,不能改变其含义
作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数操作符有一个默认的形参this,限定为第一个形参
.*,::,sizeof,?:,.注意以上5个运算符不能重载
下边的代码对==进行了重载
#define _CRT_SECURE_NO_WARNINGS 1 #includeusing namespace std; class Date { public: Date(int year = 1990, int month = 1, int day = 1) { _year = year; _month = month; _day = day; } //private: int _year; int _month; int _day; }; bool operator==(const Date& d1, const Date& d2) { return d1._year == d2._year&&d1._month == d2._month&&d1._day == d2._day; } int main() { Date d1(2018, 10, 10); Date d2(2018, 10, 10); cout << operator==(d1, d2)< 以上代码对==运算符进行了重载,但是我们发现运算符重载成全局的就需要成员变量是共有的,这样会破坏了封装性。可以使用友元函数来解决,或者直接重载成成员函数。
//重载为成员函数 #define _CRT_SECURE_NO_WARNINGS 1 #includeusing namespace std; class Date { public: Date(int year = 1990, int month = 1, int day = 1) { _year = year; _month = month; _day = day; } //需要注意的是,左操作数是this指向的调用函数的对象 bool operator==(const Date& d2) { return _year == d2._year&&_month == d2._month&&_day == d2._day; } private: int _year; int _month; int _day; }; int main() { Date d1(2018, 10, 10); Date d2(2018, 10, 10); cout << (d1 == d2) << endl; } 赋值运算符的特点
1.参数类型为常量引用
2.返回值类型为类类型的引用
3.检测是否自己给自己赋值,如果是则不进行赋值操作
4.返回this
5.如果一个类没有显示定义赋值运算符重载,编译器会生成一个,完成对象按字节序的值拷贝
那么编译器生成的默认赋值重载函数可以完成字节序的值拷贝了,我们还需要自己实现吗?#define _CRT_SECURE_NO_WARNINGS 1 #include#include using namespace std; class String { private: char* _str; public: String(const char* str = "jack") { _str = (char*)malloc(strlen(str) + 1); strcpy(_str, str); } ~String() { cout << "~string()" << endl; free(_str); } }; int main() { String s1("hello"); // String s2(s1); String s2("nihao"); s1 = s2; } s2赋值给s1之后,他们指向的是同一块地址空间,但是被析构两次,所以会引起程序崩溃。需要使用深拷贝来解决该问题。
3、const成员const修饰类的成员函数
将const修饰的类成员函数称为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
取地址及const取地址操作符重载
这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况才需要重载,如想让别人获取到指定的内容



