条款 21:必须返回对象时,别妄想返回其 reference
核心思想
C++ 中绝不要返回指针或引用指向以下几种对象:
-
局部栈对象 (local stack object)
- 当函数返回时,栈对象会被销毁,指针或引用将指向一个无效的内存地址,导致未定义行为。
-
堆分配对象 (heap-allocated object)
- 返回指针或引用可能导致资源泄漏,因为调用者必须记得手动释放资源,容易出错。
-
局部静态对象 (local static object)
- 如果同时需要多个这样的对象,返回引用会导致数据竞争和意外的共享状态问题。
推荐解决方案
当必须返回一个对象时,建议 返回值对象,这样可以依赖编译器的 返回值优化 (RVO) 或 移动语义 来避免性能损耗。
示例代码
// 安全的返回对象示例 inline const Rational operator* (const Rational& lhs, const Rational& rhs) { return Rational(lhs.n * rhs.n, lhs.d * rhs.d); }
上述代码返回了一个新的 Rational
对象,而不是返回一个指向临时对象的指针或引用。这种方式既直观又安全,依赖编译器优化,不会导致性能损耗。
禁止的用法
以下代码示例会引发问题:
- 返回局部栈对象的引用
const Rational& multiply(const Rational& lhs, const Rational& rhs) { Rational result(lhs.n * rhs.n, lhs.d * rhs.d); // 栈对象 return result; // 返回的引用无效 }
- 问题:
result
是局部栈对象,函数返回后被销毁,引用将指向无效内存。
- 返回堆对象的引用
const Rational& multiply(const Rational& lhs, const Rational& rhs) { Rational* result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d); // 堆分配 return *result; // 调用者需要手动释放 }
- 问题:调用者必须记得释放
result
,否则会造成内存泄漏。
- 返回静态局部对象的引用
const Rational& multiply(const Rational& lhs, const Rational& rhs) { static Rational result(lhs.n * rhs.n, lhs.d * rhs.d); // 静态对象 return result; // 多次调用会共享同一对象 }
- 问题:如果函数被多次调用,不同调用会共享
result
,容易引发数据竞争和意外行为。
结论
- 遇到返回对象时,优先 返回值对象,避免使用指针或引用。
- 借助现代编译器优化,返回值对象的性能损耗可以忽略不计。