全面质量管理:企业永恒的生命线与核心竞争力
2026/7/13 0:07:14
赋值运算符operator=用于将一个已存在的对象赋值给另一个已存在的对象。在继承关系中,派生类的赋值运算符需要负责赋值基类部分和派生类新增部分。
| 对比项 | 拷贝构造函数 | 赋值运算符 |
|---|---|---|
| 调用时机 | 创建新对象时 | 已有对象赋值时 |
| 目标对象 | 不存在(新创建) | 已存在 |
| 是否需要释放旧资源 | 不需要 | 可能需要 |
| 语法 | Student b(a)或Student b = a | b = a |
派生类赋值运算符调用规则 1. 派生类赋值运算符必须赋值基类部分 ↓ 2. 如果派生类没有定义赋值运算符,编译器会自动生成 ↓ 3. 编译器生成的赋值运算符会自动调用基类的赋值运算符 ↓ 4. 如果派生类自定义了赋值运算符,编译器不会自动调用基类版本 → 必须显式调用!classPerson{int_idPerson;public:Person(intid=1):_idPerson(id){}// 没有定义 operator= → 编译器自动生成按位赋值};classStudent:publicPerson{int_snum;public:Student(intid=1,ints=1):Person(id),_snum(s){}// 没有定义 operator= → 编译器自动生成// 编译器生成的赋值运算符会自动调用基类的赋值运算符};intmain(){Studentstuda(111111,22222);Studentstudb(333333,44444);studb=studa;// 使用编译器生成的赋值运算符return0;}编译器生成的代码(概念上):
Student&operator=(constStudent&other){if(this!=&other){Person::operator=(other);// 自动调用基类赋值运算符_snum=other._snum;// 按位赋值派生类成员}return*this;}classPerson{private:int_idPerson;public:Person(intid=1):_idPerson(id){}// 基类定义了赋值运算符Person&operator=(constPerson&per){if(this!=&per){_idPerson=per._idPerson;}cout<<"Person::operator="<<endl;return*this;}};classStudent:publicPerson{private:int_snum;public:Student(intid=1,ints=1):Person(id),_snum(s){}// 没有定义 operator= → 编译器自动生成// 编译器会自动调用基类的赋值运算符};intmain(){Studentstuda(111111,22222);Studentstudb(333333,44444);cout<<"=== 赋值前 ==="<<endl;studa.PrintStudent();studb.PrintStudent();studb=studa;// 调用编译器生成的赋值运算符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 学号:22222classPerson{private:int_idPerson;public:Person(intid=1):_idPerson(id){}Person&operator=(constPerson&per){if(this!=&per){_idPerson=per._idPerson;}cout<<"Person::operator="<<endl;return*this;}};classStudent:publicPerson{private:int_snum;public:Student(intid=1,ints=1):Person(id),_snum(s){}// 正确:显式调用基类赋值运算符Student&operator=(constStudent&stud){if(this!=&stud){Person::operator=(stud);// 关键!调用基类赋值运算符_snum=stud._snum;}cout<<"Student::operator="<<endl;return*this;}};intmain(){Studentstuda(111111,22222);Studentstudb(333333,44444);studb=studa;return0;}输出:
Person::operator= ← 显式调用基类赋值运算符 Student::operator=classPerson{private:int_idPerson;public:Person(intid=1):_idPerson(id){}Person&operator=(constPerson&per){if(this!=&per){_idPerson=per._idPerson;}cout<<"Person::operator="<<endl;return*this;}};classStudent:publicPerson{private:int_snum;public:Student(intid=1,ints=1):Person(id),_snum(s){}// 错误!没有调用基类赋值运算符Student&operator=(constStudent&stud){if(this!=&stud){// Person::operator=(stud); // 遗漏了!_snum=stud._snum;}cout<<"Student::operator="<<endl;return*this;}};intmain(){Studentstuda(111111,22222);Studentstudb(333333,44444);studb=studa;// 问题:studb 的基类部分(_idPerson)没有被赋值!// studb._idPerson 仍然是 333333,而不是 111111return0;}| 场景 | 基类有 operator= | 派生类有 operator= | 派生类是否调用基类版本 | 结果 |
|---|---|---|---|---|
| 1 | 编译器生成 | 编译器生成 | 自动调用 | 正确 |
| 2 | 用户定义 | 编译器生成 | 自动调用 | 正确 |
| 3 | 用户定义 | 用户定义 | 显式调用 | 正确 |
| 4 | 用户定义 | 用户定义 | 未调用 | 错误(基类部分未赋值) |
Student&operator=(constStudent&stud){if(this!=&stud){// 忘记调用 Person::operator=(stud)_snum=stud._snum;}return*this;}// 问题:基类成员没有被赋值!Student&operator=(constStudent&stud){if(this!=&stud){operator=(stud);// 死递归!调用的是自己_snum=stud._snum;}return*this;}Student&operator=(constStudent&stud){if(this!=&stud){((Person*)this)->operator=(stud);// ! 可以但不推荐_snum=stud._snum;}return*this;}Student&operator=(constStudent&stud){if(this!=&stud){Person::operator=(stud);// 显式调用基类版本_snum=stud._snum;}cout<<"Student::operator="<<endl;return*this;}| 要点 | 说明 |
|---|---|
| 必须调用基类赋值运算符 | 派生类赋值运算符必须显式调用基类版本 |
| 语法 | Person::operator=(stud); |
| 不调用的后果 | 基类成员不会被赋值,导致对象状态不完整 |
| 编译器自动生成 | 如果派生类没有定义,编译器会自动生成并调用基类版本 |
| 与拷贝构造的区别 | 拷贝构造自动调用基类版本;赋值运算符需要显式调用 |
| 自赋值检查 | 必须先检查this != &other |