effective C++ 条款 21:必须返回对象时别妄想返回其reference
考慮有理數(shù)的class:
class Rational
{
public:
??? Rational(int numerator = 0, int denominator = 1);
protected:
private:
??? int n, d;
??? friend Rational operator*(const Rational& lhs, const Rational& rhs);
};
by value版的operator*, 你可能想改而返回reference,以根除pass-by-value的種種邪惡,省掉構(gòu)造和析構(gòu),但是,
所謂reference只是個(gè)名稱,代表某個(gè)既有對(duì)象,任何時(shí)候看到一個(gè)reference聲明式,你都應(yīng)該立刻問(wèn)自己,它的另一個(gè)名稱是什么?
如果operator*要返回一個(gè)reference指向如此數(shù)值,他必須自己創(chuàng)建那個(gè)Rational對(duì)象。
const Rational& operator*(const Rational& lhs, const Rational& rhs)
{
??? Rational result(lhs.n * rhs.n, lhs.d * rhs.d); //警告!糟糕的代碼!
??? return result;
}
你的目標(biāo)是要避免調(diào)用構(gòu)造函數(shù),而result卻必須像任何對(duì)象一樣地由構(gòu)造函數(shù)構(gòu)造起來(lái)。更嚴(yán)重的是result是一個(gè)local對(duì)象,而local對(duì)象在函數(shù)退出前被銷(xiāo)毀了。因此這個(gè)返回的reference指向的是一個(gè)“從前的”Rational;如今已經(jīng)被銷(xiāo)毀了。任何調(diào)用者甚至對(duì)此函數(shù)的返回值做任何一點(diǎn)點(diǎn)運(yùn)用,都會(huì)陷入“無(wú)定義行為”的惡地。
于是考慮在heap內(nèi)構(gòu)造一個(gè)對(duì)象, 并返回reference指向它。
const Rational& operator*(const Rational& lhs, const Rational& rhs)
{
??? Rational* result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d); //更糟的代碼!
??? return *result;
}
你還是必須付出一個(gè)構(gòu)造函數(shù)調(diào)用的代價(jià),此外又有另一個(gè)問(wèn)題:誰(shuí)該對(duì)著被你new出來(lái)的對(duì)象實(shí)施delete?
Rational w, x, y, z;
w = x * y * z; //與operator*(operator*(x, y), z)相同
同一個(gè)語(yǔ)句調(diào)用了兩次operator*, 因而兩次使用new,也就需要兩次delete。但卻沒(méi)有合理的辦法讓operator*使用者進(jìn)行delete調(diào)用, 因?yàn)闆](méi)有合理的辦法讓他們?nèi)〉給perator*返回的reference背后隱藏的那個(gè)指針。這絕對(duì)導(dǎo)致資源泄漏。
或許你想到一種辦法可以避免任何構(gòu)造函數(shù)被調(diào)用。“讓operator*返回reference指向一個(gè)被定義于函數(shù)內(nèi)部的static Rational對(duì)象”
const Rational& operator*(const Rational& lhs, const Rational& rhs)
{
??? static Rational result; //又是一堆爛代碼。
??? result = ...; //lhs*rhs
??? return result;
}
就像所有用上static對(duì)象的設(shè)計(jì)一樣, 這個(gè)也立刻造成我們對(duì)多線程安全性的疑慮。只是顯而易見(jiàn)的弱點(diǎn)。更深層的瑕疵:
bool operator=(const Rational& lhs, const Rational& rhs);
Rational a, b, c, d;
if ((a*b) == (c*d)){
??? ...
}else{
??? ...
}
表達(dá)式if ((a*b) == (c*d))總是被核算為true, 不論a,b, c,d是什么。
在operator==被調(diào)用前,已有兩個(gè)operator*調(diào)用式起作用,每個(gè)都返回operator*內(nèi)部定義的static Rational對(duì)象的reference,這個(gè)將“operator*內(nèi)的static Rational對(duì)象值”和“operator*內(nèi)的static Rational對(duì)象值”比較,不相等才怪。(雖然兩次調(diào)用operator*都改變了static Rational對(duì)象的值, 但是返回的reference, 調(diào)用端看到的永遠(yuǎn)是static Rational的現(xiàn)值)。
一個(gè)“必須返回新對(duì)象”的函數(shù)的正確寫(xiě)法:就讓那個(gè)函數(shù)返回一個(gè)新對(duì)象。
inline const Rational operator*(const Rationa& lhs, const Rational& rhs)
{
??? return Rational(lhs.n * rhs.n, lhs.d * rhs.d);
}
絕不要返回pointer或reference指向一個(gè)local stack對(duì)象, 或返回reference指向一個(gè)heap-allocated對(duì)象, 或返回pointer或reference指向一個(gè)local static對(duì)象而有可能同時(shí)需要多個(gè)這樣的對(duì)象。條款4已經(jīng)為“在單線程環(huán)境中合理返回reference指向一個(gè)local static對(duì)象”提供了一份設(shè)計(jì)實(shí)例(singleton)
轉(zhuǎn)載于:https://www.cnblogs.com/lidan/archive/2012/01/17/2325108.html
總結(jié)
以上是生活随笔為你收集整理的effective C++ 条款 21:必须返回对象时别妄想返回其reference的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 迷失在小镇上的日记(16)
- 下一篇: JQuery图表插件Highcharts