当前位置: 首页 > news >正文

编写派生的赋值运算符重载函数

编写派生的赋值运算符重载函数

一、核心概念

赋值运算符operator=用于将一个已存在的对象赋值给另一个已存在的对象。在继承关系中,派生类的赋值运算符需要负责赋值基类部分派生类新增部分

二、赋值运算符 与 拷贝构造函数

对比项拷贝构造函数赋值运算符
调用时机创建新对象时已有对象赋值时
目标对象不存在(新创建)已存在
是否需要释放旧资源不需要可能需要
语法Student b(a)Student b = ab = a

三、派生类赋值运算符的调用规则

3.1 规则总结

派生类赋值运算符调用规则 1. 派生类赋值运算符必须赋值基类部分 ↓ 2. 如果派生类没有定义赋值运算符,编译器会自动生成 ↓ 3. 编译器生成的赋值运算符会自动调用基类的赋值运算符 ↓ 4. 如果派生类自定义了赋值运算符,编译器不会自动调用基类版本 → 必须显式调用!

四、四种场景详细分析

4.1 场景一:基类和派生类都没有定义赋值运算符

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;}

4.2 场景二:基类定义了赋值运算符,派生类没有定义

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 学号:22222

4.3 场景三:基类和派生类都定义了赋值运算符(正确做法)

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;return0;}

输出

Person::operator= ← 显式调用基类赋值运算符 Student::operator=

4.4 场景四:派生类定义了赋值运算符,但没有调用基类版本(危险!)

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用户定义用户定义未调用错误(基类部分未赋值)

六、错误写法与正确写法对比

6.1 错1:忘记调用基类赋值运算符

Student&operator=(constStudent&stud){if(this!=&stud){// 忘记调用 Person::operator=(stud)_snum=stud._snum;}return*this;}// 问题:基类成员没有被赋值!

6.2 错2:无穷递归引起栈溢出

Student&operator=(constStudent&stud){if(this!=&stud){operator=(stud);// 死递归!调用的是自己_snum=stud._snum;}return*this;}

6.3 错3:错误的类型转换

Student&operator=(constStudent&stud){if(this!=&stud){((Person*)this)->operator=(stud);// ! 可以但不推荐_snum=stud._snum;}return*this;}

6.4 正确写法

Student&operator=(constStudent&stud){if(this!=&stud){Person::operator=(stud);// 显式调用基类版本_snum=stud._snum;}cout<<"Student::operator="<<endl;return*this;}

七、关键要点总结

要点说明
必须调用基类赋值运算符派生类赋值运算符必须显式调用基类版本
语法Person::operator=(stud);
不调用的后果基类成员不会被赋值,导致对象状态不完整
编译器自动生成如果派生类没有定义,编译器会自动生成并调用基类版本
与拷贝构造的区别拷贝构造自动调用基类版本;赋值运算符需要显式调用
自赋值检查必须先检查this != &other
http://www.jsqmd.com/news/1177005/

相关文章:

  • go2_ros2_sdk进阶:使用CycloneDDS实现Unitree GO2机器人的以太网通信
  • Quartus Prime 23.1 工程文件结构规划:4文件夹法管理 RTL/仿真/约束/文档
  • RapidOCR技术架构深度解析:跨平台多引擎OCR解决方案的设计哲学与实践
  • 从零构建AI量化交易系统:5大核心技术实战指南
  • 从0到1构建带掩码的表单:react-native-text-input-mask企业级应用案例
  • 我让AI替我回了1个月消息,每天省下2小时:开源个人助手完全指南
  • Midjourney企业版开通全流程(含SAML/SCIM/审计日志配置):技术负责人必须掌握的12项合规要点
  • 2026 宜君县黄金回收盘点,城区乡镇优选金铂仓华金汇福金阁正规门店 - 福金阁黄金回收
  • 青岛封阳台门窗哪家好?2026本地实测:选对品牌,不漏水、隔音强、更安全 - Gsydold
  • qmqtt vs 其他MQTT库:为什么它是Qt项目的最佳选择?终极指南
  • MiGPT数据持久化终极指南:让小爱音箱拥有真正的记忆
  • Jacobin JVM类加载机制详解:从字节码解析到方法区管理
  • pz 变量系统完全指南:掌握 s、n、b、count 等核心变量的使用技巧
  • 终极免费数据库设计工具:如何在5分钟内完成专业级数据库可视化设计
  • BUUCTF [WUSTCTF2020]level3
  • 2026年7月最新西安宝玑官方售后服务网点地址及客服电话一览 - 亨得利钟表维修中心
  • 2026成都装修口碑实力双优榜:七家品牌全解析 - 推荐官
  • PARD-Qwen3-0.6B与vLLM集成教程:实现311.5 tokens/s的终极性能优化
  • MaterialValues实战案例:如何快速构建符合Material Design规范的登录界面
  • npm-security-best-practices与现代包管理器:npm、pnpm、yarn和bun的安全配置对比
  • 2026 广东江门市全区域彩钢瓦修缮公司 TOP4 权威推荐|彩钢瓦翻新 / 防水补漏 / 除锈喷漆优选指南 + 避坑攻略 - 本地便民网
  • WaitingDots与Android TV:打造专业级连接等待动画的终极指南
  • Perplexity提示词工程实战手册:27个经A/B测试验证的高响应率模板(含PDF可直接导入)
  • 3个秘籍让旧版Python在Windows上重获新生
  • Unity数据存储实战:EasySave3插件从入门到精通
  • 解锁AI文本处理新境界:Tiktoken高性能Tokenizer完全指南
  • 关于文献【ICML】
  • 2026年AI论文生成工具哪家强?5款热门工具实测对比
  • 2026年7月北京老房改造装修公司推荐:别只看名气适配老房痛点才靠谱 - 优企名品
  • Saleor Platform完全指南:如何用Docker快速搭建全功能电商开发环境