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

总结:

绝不要返回一个local栈对象的指针或引用;绝不要返回一个被分配的堆对象的引用;绝不要返回一个局部对象有可能同时需要多个这样的对象的指针或引用。

条款4中给出了“在单线程环境中合理返回局部静态对象的引用”

提出问题

一旦程序员抓住对象传值的效率隐忧,很多人就会一心一意根除传值的罪恶。他们不屈不挠地追求传引用的纯度,但他们全都犯了一个致命的错误:传递不存在的对象的引用。考虑一个用以表现有理数的类,包含一个函数计算两个有理数的乘积:

class Rational {
public:
   Rational(int numerator = 0, int denominator = 1);
    ...
private:
   int n, d; // 分子与分母
   friend const Rational operator*(const Rational& lhs, const Rational& rhs);
};

  这个版本operator* 以传值方式返回它的结果,需要付出对象的构造和析构成本。如果你能用返回一个引用来代替,就不需付出代价。但是,请记住一个引用仅仅是一个别名,一个实际存在的对象的名字。无论何时只要你看到一个引用的声明,应该立刻问自己它是什么东西的别名,因为它必定是某物的别名。以上述operator*为例,如果函数返回一个引用,它必然返回某个既有的而且包含两个对象相乘产物的Rational对象引用。

当然没有什么理由期望这样一个对象在调用operator*之前就存在。也就是说,如果你有

Rational a(1, 2);          // a = 1/2
Rational b(3, 5);          // b = 3/5
Rational c = a * b;        // c should be 3/10

   期望原本就存在一个值为3/10的有理数对象并不合理。如果operator*返回一个reference指向如此数值,它必须自己创建那个Rational对象

函数创建新对象仅有三种方法:在栈或在堆上或者在static的静态区域。

在栈空间上创建对象

如果定义一个local变量,就是在栈空间创建对象

const Rational& operator*(constRational& lhs, const Rational& rhs)
{
   Rational result(lhs.n * rhs.n, lhs.d * rhs.d);
   return result;
}  //糟糕的代码!

   本来目的是避免调用构造函数,而result却必须像任何对象一样由构造函数构造而来。这个函数返回一个指向result的引用,但是result是一个局部对象,在函数退出时被销毁了。因此这个operator*的版本不会返回指向一个Rational的引用,它返回指向一个“从前的”Rational,一个旧时的Rational,一个曾经被当做Rational但如今已经成空、发臭、败坏的残骸,因为它已经被销毁。任何调用者甚至只是对此函数的返回值做任何一点点运用,就立刻进入了未定义行为的领地。这是事实,任何返回一个指向局部变量引用(或指针)的函数都是错误的

在堆空间上创建对象

时间: 2024-10-03 01:27:05

条款21: 必须返回对象时,别妄想返回其reference的相关文章

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对

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

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

条款21:必须返回对象时,别妄想返回其reference 引用只是对象的一种别名当使用引用的时候,请确认他的另一个身份在哪? class Rational { public: Rational(int x, int y) : m_x(x), m_y(y){} //返回const是属于类型保护,friend修饰,以后条款详说 friend const Rational operator + (const Rational &lhs, const Rational &rhs) { Ration

条款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*(cons

Effective C++:条款12:复制对象时勿忘其每一个成分

(一) 一个继承体系的声明: class Date {...}; class Customer { public: ... private: string name; Date lastTransaction; }; class PriorityCustomer : public Customer { public: PriorityCustomer(const PriorityCustomer& rhs); PriorityCustomer& operator=(const Priori

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

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

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

绝不要返回pointer或reference指向一个local stack对象,或返回reference指向一个heap-allocated对象,或返回pointer或reference指向一个local static对象而有可能同时需要多个这样的对象.条款4已经为“在单线程环境中合理返回reference指向一个local static对象”提供了一份设计实例.

原则21:必须返回对象时,别妄想返回其引用

http://www.jianshu.com/p/35f26eea6cb3 Effective C++中第21个原则,因为引用是要指向某已存在的对象的,但如果该对象某一瞬间突然消失了,这个引用被架空了,那就出错了.为了证实这一点作者举了一个有理数相乘的例子.有这个一个有理数类,其中有一个有理数相乘的成员函数,该成员函数返回该有理数类的对象.在此例中该对象是一个本地对象,什么叫本地对象呢?就是一个普通的,局部的对象,它随着作用域的结束而被自动销毁.因为具备这一性质,一旦你把这个函数的返回值赋给某一