赋值运算符重载返回Class、Class&、void的区别及返回必要性解析
赋值运算符重载:三种返回类型的区别与返回值意义
问题背景
给出的深拷贝Club类赋值运算符重载代码如下:
//deep copies one object into another Club& Club::operator= (Club& obj) { this->members_ = std::unique_ptr <Member[]> (new (std::nothrow) Member[obj.num_members_]); if (members_ != nullptr) { this->num_members_ = obj.num_members_; for (int i = 0; i < obj.num_members_; i++) { this->members_[i] = obj.members_[i]; } } else { std::cout << "Memory could not be allocated.\n"; this->num_members_ = 0; } return *this; }
疑问点:
- 使用
Club(值返回)、Club&(引用返回)、void作为返回类型时代码均可运行,三者有何区别? - 执行
club200 = club200a;时已通过this修改了对象,未存储返回值,为何需要返回地址或值? - 知道引用可用于运算符链式调用,但返回值实际会发生什么?
- 教授要求按值返回,该如何理解?
一、三种返回类型的核心区别
1. 返回void
- 行为:赋值操作仅修改当前对象(
*this)的状态,完成后不返回任何值。 - 限制:完全禁用链式赋值(比如
clubA = clubB = clubC;会直接编译报错),也无法将赋值结果作为右值传递给其他函数(比如func(clubA = clubB);)。 - 场景:仅满足最基础的赋值需求,但不符合C++中赋值运算符的常规语义,实际开发中很少使用。
2. 返回Club(值返回)
- 行为:返回当前对象的副本——会调用
Club的拷贝构造函数,把*this复制一份后返回。 - 代价:额外的拷贝开销(如果类成员多、深拷贝逻辑复杂,性能损耗会很明显)。
- 特性:支持链式赋值,但链式中每一步都会产生临时对象。比如
clubA = clubB = clubC;会先执行clubB = clubC,返回clubB的副本,再把这个副本赋值给clubA。 - 注意:如果类没有正确实现拷贝构造函数,值返回可能会导致浅拷贝问题,引发内存泄漏或重复释放。
3. 返回Club&(引用返回)
- 行为:返回当前对象的左值引用——直接指向
*this本身,无拷贝开销。 - 特性:完美支持链式赋值,且完全符合C++原生类型的赋值语义(比如
int a = b = c;就是返回引用实现的)。链式调用中clubA = clubB = clubC;会先执行clubB = clubC返回clubB的引用,再把clubC的值赋值给clubA,全程无临时对象产生。 - 优势:性能最优,是C++中赋值运算符重载的标准写法(几乎所有STL容器的赋值运算符都是返回引用)。
二、为什么需要返回值?
即使你写club200 = club200a;时没有存储返回值,返回值的存在是为了:
- 符合C++常规语义:原生类型(int、float等)的赋值操作本身会返回左值,比如
int x; (x = 5) = 10;是合法的,最终x的值为10。用户会期望自定义类的赋值行为和原生类型一致。 - 支持链式操作:除了链式赋值,还可以将赋值结果作为参数传递给函数,比如
print(clubA = clubB);,此时函数接收的是赋值后的clubA(引用返回)或其副本(值返回)。 - 代码灵活性:保留后续扩展的可能,比如未来需要在赋值后直接调用对象的成员函数:
(clubA = clubB).addMember();,引用返回可以直接操作原对象,值返回则只能操作临时副本(操作完即销毁,意义不大)。
三、链式调用中返回值的实际行为
以clubA = clubB = clubC;为例:
- 如果返回
Club&:- 先调用
clubB.operator=(clubC),修改clubB的状态,返回clubB的引用; - 再调用
clubA.operator=(返回的clubB引用),修改clubA的状态,返回clubA的引用; - 全程无临时对象,所有操作直接作用于原对象。
- 先调用
- 如果返回
Club:- 调用
clubB.operator=(clubC),修改clubB,返回clubB的副本(临时对象); - 调用
clubA.operator=(这个临时对象),修改clubA,返回clubA的副本; - 临时对象在语句结束后销毁,造成额外的拷贝和析构开销。
- 调用
- 如果返回
void:
编译器直接报错,因为clubB = clubC没有返回值,无法作为clubA = ...的右值。
内容的提问来源于stack exchange,提问作者happy
相关产品推荐
相关产品推荐

