概念概述
赋值运算符重载的特点:
- 成员函数:赋值运算符重载必须定义为类的成员函数。
- 参数:建议将参数声明为
const
类型的类引用,以避免不必要的拷贝。- 返回值:应有返回值,且建议为当前类类型的引用,这样可以支持连续赋值操作,并提高效率。
编译器自动生成的赋值运算符:
- 如果没有显式实现赋值运算符重载,编译器会提供一个默认实现。
- 默认赋值运算符对内置类型成员变量执行值拷贝或浅拷贝。
- 对自定义类型成员变量,会调用其赋值运算符重载函数。
特定情况下的赋值运算符重载:
- 对于像
Date
这样只有内置类型成员的类,编译器自动生成的赋值运算符通常足够使用。- 对于像
Stack
这样包含指向资源的成员的类,需要自定义赋值运算符以实现深拷贝。- 对于像
MyQueue
这样包含自定义类型成员的类,如果这些成员的赋值运算符已经正确实现,通常不需要为MyQueue
显式实现赋值运算符重载。额外技巧:
- 如果一个类显式实现了析构函数并释放资源,通常也需要显式实现赋值运算符重载,以确保资源被正确管理。
赋值运算符重载的使用以及注意事项
1,有返回值,建议写成const类类型的引用。因为C++规定类类型的传值传参,会调用拷贝构造,传递引用会减少拷贝(提高效率)
2,连续赋值的返回值
d3=d1;
返回值是d3,所以我们写代码的时候,要注意返回值,很可能是this
第一次赋值
第二次赋值
3,当返回的节点是d2,如何实现返回d2(用this,this本身指向的就是返回值)(这里注意:有返回值就支持连续赋值,但是返回的时候,需要注意)
注意:这里返回是可以用引用返回的,因为赋值运算符重载是两个已有的对象。出去作用域是依旧存在的(这里代码是传值返回,会增加拷贝)
4,这里还有一个问题,就是d1可以复制給給d1,所以为了防止自己給自己赋值 ,会进行判断
代码实现
//.h文件 //运算符重载,比较大小的实现,不在类里面实现 class Date { public: Date(int year = 1000, int month = 1, int day = 1);//声明给,定义不给 Date(Date& d); void print(); bool operator<(const Date& d); //比较日期大小(重载为成员函数) Date& operator++(); Date& operator++(int); //赋值运算符重载(这里是可以加上const的,因为这里改变的是this不是d) Date& operator=(const Date& d); private: int _year; int _month; int _day; }; //.cpp实现文件 #include"类和对象的通篇实现.h" //构造函数的实现(全缺省构造函数的实现) Date::Date(int year, int month, int day) { _year = year; _month = month; _day = day; } //拷贝构造的实现 Date::Date(Date& d) { _year = d._year; _month = d._month; _day = d._day; } //打印函数的实现 void Date::print() { cout << _year << "/" << _month << "/" << _day << endl; } //日期类的比较日期的大小,this-> < d.year 此时返回 bool Date::operator<(const Date& d) { //比较年 if (this->_year < d._year) { return true; } else { if (this->_year == d._year && this->_month < d._month)//比较月 { return true; } else if (this->_year == d._year && this->_month < d._month && this->_day < d._day)//比较日 { return true; } } return false; } //前置++ Date& Date::operator++() { this->_day += 1; return *this; } //后置++ Date& Date::operator++(int) { Date tmp = *this; this->_day += 1; return tmp; } //赋值运算符重载(这里是可以加上const的,因为这里改变的是this不是d) Date& Date::operator=(const Date& d) { //地址不一样才进行赋值 if (this != &d) { _year = d._year; _month = d._month; _day = d._day; } return *this; } //.cpp测试文件 //运算符重载,比较大小 int main() { //构造和拷贝构造函数的使用 Date d1(1000, 2, 1); d1.print(); Date d2 = d1; d2.print(); //比较年月日 Date d3(999, 1, 1); bool ret1 = d1.operator<(d3); cout << ret1 << endl; Date d4(1100, 1, 1); bool ret2 = d1.operator<(d4); cout << ret2 << endl; //前置++和后置++ cout << "前置++和后置++" << endl; d4.print();//1100 ,1,1 d4.operator++(); d4.print(); Date ret = d4.operator++(1); ret.print(); d4.print(); //赋值运算符重载 cout << "赋值运算符重载" << endl; Date d5(1, 1, 1); d5.print(); d5 = d4;//这样写也是对的 //d5.operator=(d4);//这样写也是对的 d5.print(); return 0; }
赋值运算符重载什么时候需要自己实现
注意:默认的赋值运算符重载也会完成浅拷贝(有资源,就需要自己完成赋值运算符重载)
1,内置类型不指向什么资源,不需要自己实现,编译器自动生成的就可以实现
2,栈和深拷贝,都需要自己写,和拷贝构造非常相似
3,如果写了析构函数,那么赋值运算符重载就需要自己写
几个函数之间进行对比
1、构造一般都需要自己写,自己传参定义初始化
2、析构,构造时有资源申请(如malloc或者fopen)等,就需要显示写析构函数
3、拷贝构造和赋值重载,显示写了析构,内部管理资源,就需要显示实现深拷贝