编写派生的赋值运算符重载函数
编写派生的赋值运算符重载函数一、核心概念赋值运算符operator用于将一个已存在的对象赋值给另一个已存在的对象。在继承关系中派生类的赋值运算符需要负责赋值基类部分和派生类新增部分。二、赋值运算符 与 拷贝构造函数对比项拷贝构造函数赋值运算符调用时机创建新对象时已有对象赋值时目标对象不存在新创建已存在是否需要释放旧资源不需要可能需要语法Student b(a)或Student b ab a三、派生类赋值运算符的调用规则3.1 规则总结派生类赋值运算符调用规则 1. 派生类赋值运算符必须赋值基类部分 ↓ 2. 如果派生类没有定义赋值运算符编译器会自动生成 ↓ 3. 编译器生成的赋值运算符会自动调用基类的赋值运算符 ↓ 4. 如果派生类自定义了赋值运算符编译器不会自动调用基类版本 → 必须显式调用四、四种场景详细分析4.1 场景一基类和派生类都没有定义赋值运算符classPerson{int_idPerson;public:Person(intid1):_idPerson(id){}// 没有定义 operator → 编译器自动生成按位赋值};classStudent:publicPerson{int_snum;public:Student(intid1,ints1):Person(id),_snum(s){}// 没有定义 operator → 编译器自动生成// 编译器生成的赋值运算符会自动调用基类的赋值运算符};intmain(){Studentstuda(111111,22222);Studentstudb(333333,44444);studbstuda;// 使用编译器生成的赋值运算符return0;}编译器生成的代码概念上Studentoperator(constStudentother){if(this!other){Person::operator(other);// 自动调用基类赋值运算符_snumother._snum;// 按位赋值派生类成员}return*this;}4.2 场景二基类定义了赋值运算符派生类没有定义classPerson{private:int_idPerson;public:Person(intid1):_idPerson(id){}// 基类定义了赋值运算符Personoperator(constPersonper){if(this!per){_idPersonper._idPerson;}coutPerson::operatorendl;return*this;}};classStudent:publicPerson{private:int_snum;public:Student(intid1,ints1):Person(id),_snum(s){}// 没有定义 operator → 编译器自动生成// 编译器会自动调用基类的赋值运算符};intmain(){Studentstuda(111111,22222);Studentstudb(333333,44444);cout 赋值前 endl;studa.PrintStudent();studb.PrintStudent();studbstuda;// 调用编译器生成的赋值运算符cout\n 赋值后 endl;studa.PrintStudent();studb.PrintStudent();return0;}输出Person(int) Student(int, int) Person(int) Student(int, int) 赋值前 身份证号111111 学号22222 身份证号333333 学号44444 Person::operator ← 自动调用基类赋值运算符 赋值后 身份证号111111 学号22222 身份证号111111 学号222224.3 场景三基类和派生类都定义了赋值运算符正确做法classPerson{private:int_idPerson;public:Person(intid1):_idPerson(id){}Personoperator(constPersonper){if(this!per){_idPersonper._idPerson;}coutPerson::operatorendl;return*this;}};classStudent:publicPerson{private:int_snum;public:Student(intid1,ints1):Person(id),_snum(s){}// 正确显式调用基类赋值运算符Studentoperator(constStudentstud){if(this!stud){Person::operator(stud);// 关键调用基类赋值运算符_snumstud._snum;}coutStudent::operatorendl;return*this;}};intmain(){Studentstuda(111111,22222);Studentstudb(333333,44444);studbstuda;return0;}输出Person::operator ← 显式调用基类赋值运算符 Student::operator4.4 场景四派生类定义了赋值运算符但没有调用基类版本危险classPerson{private:int_idPerson;public:Person(intid1):_idPerson(id){}Personoperator(constPersonper){if(this!per){_idPersonper._idPerson;}coutPerson::operatorendl;return*this;}};classStudent:publicPerson{private:int_snum;public:Student(intid1,ints1):Person(id),_snum(s){}// 错误没有调用基类赋值运算符Studentoperator(constStudentstud){if(this!stud){// Person::operator(stud); // 遗漏了_snumstud._snum;}coutStudent::operatorendl;return*this;}};intmain(){Studentstuda(111111,22222);Studentstudb(333333,44444);studbstuda;// 问题studb 的基类部分_idPerson没有被赋值// studb._idPerson 仍然是 333333而不是 111111return0;}五、四种场景对比总结场景基类有 operator派生类有 operator派生类是否调用基类版本结果1编译器生成编译器生成自动调用正确2用户定义编译器生成自动调用正确3用户定义用户定义显式调用正确4用户定义用户定义未调用错误基类部分未赋值六、错误写法与正确写法对比6.1 错1忘记调用基类赋值运算符Studentoperator(constStudentstud){if(this!stud){// 忘记调用 Person::operator(stud)_snumstud._snum;}return*this;}// 问题基类成员没有被赋值6.2 错2无穷递归引起栈溢出Studentoperator(constStudentstud){if(this!stud){operator(stud);// 死递归调用的是自己_snumstud._snum;}return*this;}6.3 错3错误的类型转换Studentoperator(constStudentstud){if(this!stud){((Person*)this)-operator(stud);// 可以但不推荐_snumstud._snum;}return*this;}6.4 正确写法Studentoperator(constStudentstud){if(this!stud){Person::operator(stud);// 显式调用基类版本_snumstud._snum;}coutStudent::operatorendl;return*this;}七、关键要点总结要点说明必须调用基类赋值运算符派生类赋值运算符必须显式调用基类版本语法Person::operator(stud);不调用的后果基类成员不会被赋值导致对象状态不完整编译器自动生成如果派生类没有定义编译器会自动生成并调用基类版本与拷贝构造的区别拷贝构造自动调用基类版本赋值运算符需要显式调用自赋值检查必须先检查this ! other