条款23: 必须返回一个对象时不要试图返回一个引用

class rational {
public:
  rational(int numerator = 0, int denominator = 1);

  ...

private:
  int n, d;              // 分子和分母

friend
  const rational                      // 参见条款21:为什么
    operator*(const rational& lhs,    // 返回值是const
              const rational& rhs)
};

inline const rational operator*(const rational& lhs,
                                const rational& rhs)
{
  return rational(lhs.n * rhs.n, lhs.d * rhs.d);
}

请记住,引用只是一个名字,一个其它某个已经存在的对象的名字。无论何时看到一个引用的声明,就要立即问自己:它的另一个名字是什么呢?因为它必然还有另外一个什么名字。拿operator*来说,如果函数要返回一个引用,那它返回的必须是其它某个已经存在的rational对象的引用,这个对象包含了两个对象相乘的结果。

但,期望在调用operator*之前有这样一个对象存在是没道理的。

如果operator* 一定要返回这样一个数的引用,就必须自己创建这个数的对象。一个函数只能有两种方法创建一个新对象:在堆栈里或在堆上。在堆栈里创建对象时伴随着一个局部变量的定义,采用这种方法,就要这样写operator*:

// 写此函数的第一个错误方法
inline const rational& operator*(const rational& lhs,
                                 const rational& rhs)
{
  rational result(lhs.n * rhs.n, lhs.d * rhs.d);
  return result;
}

这个方法应该被否决,因为我们的目标是避免构造函数被调用,但result必须要象其它对象一样被构造。另外,这个函数还有另外一个更严重的问题,它返回的是一个局部对象的引用。

基于堆的对象是通过使用new产生的,所以应该这样写operator*:

// 写此函数的第二个错误方法
inline const rational& operator*(const rational& lhs,
                                 const rational& rhs)
{
  rational *result =
    new rational(lhs.n * rhs.n, lhs.d * rhs.d);
  return *result;
}

首先,你还是得负担构造函数调用的开销,因为new分配的内存是通过调用一个适当的构造函数来初始化的(见条款5和m8)。另外,还有一个问题:谁将负责用delete来删除掉new生成的对象呢?

实际上,这绝对是一个内存泄漏。即使可以说服operator*的调用者去取函数返回值地址,然后用delete去删除它(操作很麻烦——条款31展示了这样的代码会是什么样的),但一些复杂的表达式会产生没有名字的临时值,程序员是不可能得到的。例如:

rational w, x, y, z;

w = x * y * z;

两个对operator*的调用都产生了没有名字的临时值,程序员无法看到,因而无法删除。

条款23: 必须返回一个对象时不要试图返回一个引用

时间: 2024-08-10 06:11:53

条款23: 必须返回一个对象时不要试图返回一个引用的相关文章

C++常见误区1 -- 必须返回一个对象时不要试图返回一个引用

1 #include <iostream> 2 3 class Person 4 { 5 public: 6 Person(){} 7 8 void setAge(int age) { this->m_iAge = age; } 9 10 int getAge() { return this->m_iAge; } 11 12 private: 13 int m_iAge; 14 }; 15 16 Person* CreatePerson() 17 { 18 Person p; 19

Effective C++:条款21:必须返回对象时别妄想返回其reference

(一) 一定要避免传递一些references去指向其实并不存在的对象. 看下面这个类: class Rational { public: Rational(int numerator = 0, int denominator = 1); private: int n, d; friend const Rational operator*(const Rational& lhs, const Rational& rhs); }; 这个函数不能返回引用, (二)在stack中分配local对

在必须返回一个对象时,不要去尝试返回一个引用

一旦程序员把注意力都转向了对象传值方式隐含的效率问题(参见第 20 条)时,许多人都变成了极端的“改革运动者”,他们对传值方法采取斩草除根的态度,在他们不屈不挠追求传递引用方式的纯粹性的同时,他们也犯下了致命的错误:有时候传递的引用所指向的对象并不存在.这决不是一件好事情. 请看下面的示例,其中的 Rational 类用来表示有理数,其中还包括一个函数来计算两个有理数的乘积: class Rational {public:  Rational(int numerator = 0, int den

条款12:复制对象时请勿忘每一个成分

条款12:复制对象时请勿忘每一个成分 当为一个类实现自己的构造函数,相关赋值函数,析构函数,则必须有责任对类中的每一个成员进行初始化.赋值.释放.因此:如果为一个类添加一个成员,就必须同时相应修改上面几类函数. 看一个简单的类 class Terminal { Terminal(const int termid) : m_termId(termid) {} ~Terminal() {} Terminal(const Terminal & terminal) { this->m_termId

Effective C++ 条款21必须返回对象时,别妄想返回其reference

1. 虽然一般情况下传参使用const-by-reference-to-const比较好,但返回值有时必须是对象而不是引用: 当reference绑定到函数内创建的局部对象时,函数调用结束后引用绑定在一个不存在的对象; 当reference绑定到堆内存对象时,函数调用后比较容易忽略内存释放,特别对于以下表达式:w=x*y*z.如果operator*返回指向堆内存的引用,那没内存泄露是必然的; 当reference绑定到函数内部创建的static对象时,对于以下表达式if(a*b==c*d)由于返

Item 21:需要返回对象时,不要返回引用 Effective C++笔记

Item 21: Don't try to return a reference when you must return an object Item 20中提到,多数情况下传引用比传值更好.追求这一点是好的,但千万别返回空的引用或指针. 一个典型的场景如下: class Rational{ int n, d; public: Raitonal(int numerator=0, int denominator=1); }; // 返回值为什么是const请参考Item 3 friend con

ibatis查询SQL语句返回一个对象,此对象包含一个List属性

可能标题说的不是很明白,具体其实就是: 我需要查询一个用户,但是这个用户拥有多张试卷,所以我将试卷这个对象,作为User对象的一个属性,这个属性为List类型.这样我在SQL查询时直接查询user表返回user对象,此user对象包含一个属性(List<Test>多张试卷). java实体类对应关系: public class Test{ private Integer userID; // 主键ID private String username; // 姓名 private String

C++对象模型之编译器如何处理函数返回一个对象

1.与经验不符的输出 我们知道,当发生以下三种情况之一时,对象对应的类的复制构造函数将会被调用: 1)对一个对象做显示的初始化操作时 2)当对象被当作参数传递给某个函数时 3)当函数返回一个类的对象时 所以,当我们设计一个函数(普通或成员函数)时,经验告诉我们,出于效率的考虑,应该尽可能返回一个对象的指针或引用,而不是直接返回一个对象.因为在直接返回一个对象可能会引起对象的复制构造过程,这意味着会发生一定量的内存复制和对象创建的动作,从而降低了程序的效率.这个设计的想法是正确的,但是实际上,当函

条款21: 必须返回对象时,别妄想返回其reference

总结: 绝不要返回一个local栈对象的指针或引用:绝不要返回一个被分配的堆对象的引用:绝不要返回一个局部对象有可能同时需要多个这样的对象的指针或引用. 条款4中给出了“在单线程环境中合理返回局部静态对象的引用” 提出问题 一旦程序员抓住对象传值的效率隐忧,很多人就会一心一意根除传值的罪恶.他们不屈不挠地追求传引用的纯度,但他们全都犯了一个致命的错误:传递不存在的对象的引用.考虑一个用以表现有理数的类,包含一个函数计算两个有理数的乘积: class Rational { public: Rati