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

Item10--令赋值操作符返回一个

1.令赋值(assignment)操作符返回一个&

1. 核心目的

是为了实现 “连锁赋值”(Chained Assignment)

在 C++ 中,对于内置类型(如 int),我们习惯这样写代码:

int x, y, z;
x = y = z = 15; // 连锁赋值

由于赋值运算符是右结合(right-associative)的,上面的代码解析为: x = (y = (z = 15)); 为了让自定义的类也能支持这种写法,你的 operator= 必须返回一个指向对象本身的引用。

2. 标准写法示例

这是一个“协议”(Convention),虽然不是强制的语法要求,但所有的标准库类型(如 std::string, std::vector)都遵守这个协议。

class Widget {
public:// 1. 返回类型必须是当前类的引用 (Widget&)Widget& operator=(const Widget& rhs) {// ...这里执行赋值操作(如拷贝数据)...// 2. 返回 *this (即当前对象的引用)return *this;}
};

3. 使用场景演示

如果你遵守了 Item 10,用户就可以这样写:

Widget w1, w2, w3;
// ... 初始化 w3 ...w1 = w2 = w3; 
// 过程:
// 1. w2.operator=(w3) 被调用,返回 w2 的引用。
// 2. w1.operator=(w2) 被调用(参数是上一步返回的 w2),返回 w1 的引用。

4. 扩展适用范围

这个规则不仅适用于标准的赋值运算符 =,也适用于所有赋值相关的运算(compound assignment operators):

  • +=
  • -=
  • *=
  • /=
class Widget {
public:Widget& operator+=(const Widget& rhs) {// ... 执行加法 ...return *this; // 依然要返回 *this}
};

5. 如果不遵守会怎样?

  • 如果返回 void:代码编译可能没问题,但用户无法使用 a = b = c 这样的写法,这会让你的类显得“格格不入”,不符合 C++ 直觉。
  • 如果返回对象(By Value):虽然支持连锁赋值,但会导致不必要的拷贝构造(Copy Constructor)调用,严重影响性能。
  • 如果返回 const reference:虽然避免了拷贝,但会阻止某些合法的(虽然少见)操作,例如 (a = b).func() 可能会因为 const 限制而失败。

总结

这就是一个单纯的“从众”原则:既然 int 这样做,既然标准库这样做,为了让你的类好用且符合直觉,你也应该这样做。

记住模版:

ClassName& operator=(const ClassName& rhs) {// ...return *this;
}

2.如果返回 `const reference‘的问题

1. 实际代码限制:链式调用与立即修改

假设我们有一个类 Widget,并且它的赋值操作符返回 const Widget&

C++

class Widget {
public:// 【错误示范】返回 const 引用const Widget& operator=(const Widget& rhs) {// ... 赋值逻辑 ...return *this; }// 一个普通的非 const 成员函数void setup() { // 做一些初始化工作 }
};

场景一:赋值后立即调用方法 (Method Chaining)

这是一种比较常见的写法,尤其是在需要对赋值后的对象立即进行某些操作时。

C++

Widget w1, w2;// 意图:把 w2 赋给 w1,然后立即初始化 w1
(w1 = w2).setup(); // ❌ 编译报错!
// 原因:
// 1. (w1 = w2) 返回的是 const Widget&(只读引用)。
// 2. setup() 是一个非 const 函数。
// 3. C++ 禁止在 const 对象上调用非 const 函数。

如果 operator= 返回的是普通的 Widget&,这行代码是完全合法的。

场景二:作为非 const 函数的参数

假设有一个函数需要修改传入的对象:

C++

void logAndModify(Widget& w); // 接受非 const 引用Widget a, b;
// 意图:把 b 赋给 a,然后把 a 传进去处理
logAndModify(a = b); // ❌ 编译报错!
// 原因:logAndModify 需要 Widget&,但赋值表达式提供的是 const Widget&。

2. 与内置类型 (int) 的行为对比

这是 Scott Meyers 在《Effective C++》中反复强调的原则:"Do as the ints do"(像 int 那样行事)。

让我们看看 int 是怎么处理这种“奇怪”操作的:

C++

int x, y, z;
x = 0;
y = 1;
z = 2;// 写法:(x = y) = z;
// 意思是:先把 y 赋给 x,然后把 z 赋给 x。
// 最终结果:x 等于 2,y 等于 1,z 等于 2。
(x = y) = z; 

这段代码在 C++ 中是合法的(虽然逻辑上有点奇怪,但在某些宏定义或特定的算法模板中可能会出现)。

  • x = y 返回的是 x 的引用(可修改的左值)。
  • 然后 x 被再次赋值为 z

如果你的类返回 const reference

C++

Widget w1, w2, w3;
(w1 = w2) = w3; // ❌ 编译报错!
// 原因:(w1 = w2) 返回的是 const 引用,你不能给一个 const 对象赋值。

总结

虽然 (a = b) = c 这种写法在实际业务代码中写出来可能会被同事打,但 (a = b).func() 这种写法是有实际应用场景的。

为了保证你的类:

  1. 灵活:支持连式调用和后续修改。
  2. 一致:表现得像内置类型,不让使用者感到意外(Principle of Least Astonishment)。

标准做法始终是:返回 T&(non-const reference)。

http://www.jsqmd.com/news/115920/

相关文章:

  • Item9--绝不在构造和析构过程中调用虚函数
  • python django flask考研互助交流平台_c62p51fu--论文
  • 日记12.18
  • 离散化遍历
  • Ubuntu上使用VScode创建Maven项目
  • 线程(2)
  • 大规模语言模型的抽象思维与创新能力培养
  • 线程(1)
  • 方达炬〖发明超新技术〗:冰堆技术;冷极冰堆建筑技术;
  • Item6--若不想使用编译器自动生成的函数,就该明确拒绝
  • 我发现LLM解析基因数据优化抗癌药剂量,患者副作用直降40%
  • 日记12.16
  • 论文AIGC查重率高怎么办?6个降AI率工具和技巧,AI率从100%降到3%! - 还在做实验的师兄
  • PCL曲面重建——为一组点云重建凸多边形/凹多边形
  • 信息与关系:涌现的三大核心原则
  • Linux文件权限
  • 28
  • 灵遁者:量子基元理论带来的新观点
  • 【补充】远程连接学校服务器操作说明
  • 本地私有知识库新选择:访答软件真实体验分享
  • 划分dp
  • 花边服饰银发红眸者山间近景
  • 日记12,15
  • Item4--确定对象被使用前已先被初始化
  • string_view
  • 当K3s遇见RustFS:轻量级边缘存储方案的探索与实践
  • 比话降AI靠谱吗?比话能降知网AI率吗? - 还在做实验的师兄
  • 树形背包
  • 八皇后问题
  • 好用做老房换新实用门窗品牌精选指南的机构