C++箴言:必须返回对象时别返回引用

C++箴言:必须返回对象时别返回引用,第1张

C++箴言:必须返回对象时别返回引用,第2张

一旦程序员抓住了对对象值传递效率的隐忧,很多人就会变成狂热的圣战者,誓要根除值传递的罪恶,不管它隐藏的有多深。他们不懈地追求引用的纯粹性,但他们都犯了一个致命的错误:他们开始传输不存在的对象的引用。这不是一件好事。

考虑一个表示有理数并包含将两个有理数相乘的函数的类:

class Rational {
public:
Rational(int分子= 0,//为什么这个
int分母= 1见第24项);// ctor未显式声明

...

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

友:
const Rational //为什么
运算符*(const Rational& lhs,//返回类型是const
const Rational& rhs)见第3项;
};

这个版本的operator*通过值返回它的结果,如果你不担心构造和析构那个对象的代价,你就是在推卸你的专业责任。如果不是不得已,就不应该花钱买这样的对象。所以问题来了:你必须这么做吗?

哦,如果你能返回一个引用,你就不用这么做了。但是,请记住引用只是一个名字,一个实际对象的名字。每当你看到一个被引用的语句,你应该马上问自己这是某个事物的别称,因为这一定是某个事物的别称。在这个操作符*的例子中,如果函数返回一个引用,它必须返回一个对包含两个对象乘积的现有Rational对象的引用。

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

有理a(1,2);//a = 1/2
有理b(3,5);// b = 3/5

有理c = a * b;// c应该是3/10

似乎没有理由期望一个值为十分之三的有理数已经存在于那里。不是这样的。如果operator*返回对这样一个数字的引用,它必须自己创建该数字对象。

对于一个函数来说,创建一个新对象只有两种方法:在堆栈上或者在堆上。栈上的积是通过定义一个局部变量生成的。使用这种策略,您可以尝试以这种方式编写operator*:

const Rational & operator *(const Rational & lhs,//警告!错误代码!
const Rational & RHS)
{
有理结果(lhs.n * rhs.n,lhs . d * RHS . d);
返回结果;
}

您可以立即拒绝此方法,因为您的目标是避免调用构造函数,并且结果必须像任何其他对象一样构造。更严重的问题是,这个函数返回对result的引用,但result是一个局部对象,函数退出时局部对象被破坏。那么,这个版本的operator*将不会返回一个对Rational的引用——它将返回一个对以前的Rational的引用;前理性;;一个空又臭又腐败的洞,曾经是理性的象征,但再也不是尸体了,因为它已经被摧毁了。任何调用者都会立即进入未定义行为的领域,甚至没有时间看一眼这个函数的返回值。的确,任何返回局部变量引用的函数都是错误的。(这同样适用于任何返回指向局部变量的指针的函数。)

因此,让我们考虑在堆上构造一个对象并返回指向它的引用的可能性。基于堆的对象通过使用new开始存在,因此您可以编写一个基于堆的操作符*,如下所示:

const Rational & operator *(const Rational & lhs,//警告!更坏的
const Rational& rhs) //代码!
{
Rational * result = new Rational(lhs . n * RHS . n,lhs . d * RHS . d);
return * result;
}

哦,你还是要付出构造函数调用的代价,因为new分配的内存是通过调用合适的构造函数来初始化的。但是现在你有了另一个问题:谁是删除你用new制作的对象的合适人选?

即使呼叫者是认真和专注的,他们也不太可能使用这样的方案来合理地防止泄漏:

有理w,x,y,z;

w = x * y * z;//与运算符*(运算符*(x,y),z)相同

在这里,同一个语句中有两次对operator*的调用,所以new使用了两次,两次都需要使用delete来销毁。但是operator*的客户没有合理的方法来进行这些调用,因为他们没有合理的方法来获取隐藏在调用operator*返回的引用后面的指针。这是注定的资源泄露。

位律师回复
DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
白度搜_经验知识百科全书 » C++箴言:必须返回对象时别返回引用

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情