本篇內(nèi)容介紹了“c++臨時(shí)對(duì)象是什么”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
讓客戶(hù)滿(mǎn)意是我們工作的目標(biāo),不斷超越客戶(hù)的期望值來(lái)自于我們對(duì)這個(gè)行業(yè)的熱愛(ài)。我們立志把好的技術(shù)通過(guò)有效、簡(jiǎn)單的方式提供給客戶(hù),將通過(guò)不懈努力成為客戶(hù)在信息化領(lǐng)域值得信任、有價(jià)值的長(zhǎng)期合作伙伴,公司提供的服務(wù)項(xiàng)目有:域名與空間、虛擬空間、營(yíng)銷(xiāo)軟件、網(wǎng)站建設(shè)、武漢網(wǎng)站維護(hù)、網(wǎng)站推廣。
什么是臨時(shí)對(duì)象?
C++真正的臨時(shí)對(duì)象是不可見(jiàn)的匿名對(duì)象,不會(huì)出現(xiàn)在你的源碼中,但是程序在運(yùn)行時(shí)確實(shí)生成了這樣的對(duì)象.
通常出現(xiàn)在以下兩種情況:
(1)為了使函數(shù)調(diào)用成功而進(jìn)行隱式類(lèi)型轉(zhuǎn)換的時(shí)候。
傳遞某對(duì)象給一個(gè)函數(shù),而其類(lèi)型與函數(shù)的形參類(lèi)型不同時(shí),如果可以通過(guò)隱式轉(zhuǎn)化的話(huà)可以使函數(shù)調(diào)用成功,那么此時(shí)會(huì)通過(guò)構(gòu)造函數(shù)生成一個(gè)臨時(shí)對(duì)象,當(dāng)函數(shù)返回時(shí)臨時(shí)對(duì)象即自動(dòng)銷(xiāo)毀。如下例:
//計(jì)算字符ch在字符串str中出現(xiàn)的次數(shù) int countChar (const string& str, char ch); char buffer[]; char c; //調(diào)用上面的函數(shù) countChar (buffer, c);
我們看的第一個(gè)參數(shù)為char[],而函數(shù)的參數(shù)類(lèi)型為const string&,參數(shù)不一致,看看能否進(jìn)行隱式轉(zhuǎn)化,string類(lèi)有個(gè)構(gòu)造函數(shù)是可以作為隱式轉(zhuǎn)化函數(shù)(參見(jiàn)5)的。那么編譯器會(huì)產(chǎn)生一個(gè) string的臨時(shí)變量,以buffer為參數(shù)進(jìn)行構(gòu)造,那么countChar中的str參數(shù)會(huì)綁定到此臨時(shí)變量上,直到函數(shù)返回時(shí)銷(xiāo)毀。
注意這樣的轉(zhuǎn)化只會(huì)出現(xiàn)在兩種情況下:函數(shù)參數(shù)以傳值(by value)的方式傳遞 或者 對(duì)象被傳遞到一個(gè) reference-to-const 參數(shù)上。
傳值方式:
int countChar (string str, char ch); string buffer; char c; //參數(shù)通過(guò)傳值方式傳遞 countChar (buffer, c);
這種方法會(huì)調(diào)用string的拷貝構(gòu)造函數(shù)生成一個(gè)臨時(shí)變量,再將這個(gè)臨時(shí)變量綁定到str上,函數(shù)返回時(shí)進(jìn)行銷(xiāo)毀。
傳常量引用:
開(kāi)始的實(shí)例即時(shí)屬于這種情況,但一定強(qiáng)調(diào)的是傳遞的是const型引用,如將開(kāi)始函數(shù)的原型改為
int countChar (string& str, char ch);
下面調(diào)用相同,編譯器會(huì)報(bào)錯(cuò)!為什么C++設(shè)計(jì)時(shí)要求當(dāng)對(duì)象傳遞給一個(gè)reference-to-non-const 參數(shù)不會(huì)發(fā)生隱式類(lèi)型轉(zhuǎn)化呢?
下面的實(shí)例可能向你說(shuō)明這樣設(shè)計(jì)的目的:
//聲明一個(gè)將str中字符全部轉(zhuǎn)化為大寫(xiě) void toUpper (string& str); char buffer[] = "hazirguo"; toUpper(buffer); //error!!非const引用傳遞參數(shù)不能完成隱式轉(zhuǎn)化
如果編譯器允許上面的傳遞完成,那么,會(huì)生成一個(gè)臨時(shí)對(duì)象,toUpper函數(shù)將臨時(shí)變量的字符轉(zhuǎn)化為大寫(xiě),返回是銷(xiāo)毀對(duì)象,但是對(duì)buffer內(nèi)容毫無(wú)影響!程序設(shè)計(jì)的目地是期望對(duì)“非臨時(shí)對(duì)象”進(jìn)行修改,而如果對(duì)reference-to-non-cosnt對(duì)象進(jìn)行轉(zhuǎn)化,函數(shù)只會(huì)對(duì)臨時(shí)變量進(jìn)行修 改。這就是為什么C++中要禁止non-const-reference參數(shù)產(chǎn)生臨時(shí)變量的原因了。
(2)當(dāng)函數(shù)返回對(duì)象的時(shí)候。
當(dāng)函數(shù)返回一個(gè)對(duì)象時(shí),編譯器會(huì)生成一個(gè)臨時(shí)對(duì)象返回,如聲明一個(gè)函數(shù)用來(lái)合并兩個(gè)字符串:
const string strMerge (const string s1, const string s2);
大多時(shí)候是無(wú)法避免這樣的臨時(shí)變量產(chǎn)生的,但是現(xiàn)代編譯器可以將這樣的臨時(shí)變量進(jìn)行優(yōu)化掉,這樣的優(yōu)化策略中,有個(gè)所謂的“返回值優(yōu)化”,下一篇具體講解。
總結(jié):
臨時(shí)對(duì)象有構(gòu)造和析構(gòu)的成本,影響程序的效率,因此盡可能地消除它們。而更為重要的是很快地發(fā)現(xiàn)什么地方會(huì)生成臨時(shí)對(duì)象:
當(dāng)我們看到一個(gè)reference-to-const參數(shù)時(shí),極可能一個(gè)臨時(shí)對(duì)象綁定到該參數(shù)上;
當(dāng)我們看到函數(shù)返回一個(gè)對(duì)象時(shí),就會(huì)產(chǎn)生臨時(shí)對(duì)象。
參考:http://www.cnblogs.com/hazir/archive/2012/04/18/2456144.html
返回值優(yōu)化(Return Value Optimization,簡(jiǎn)稱(chēng)RVO),是這么一種優(yōu)化機(jī)制:當(dāng)函數(shù)需要返回一個(gè)對(duì)象的時(shí)候,如果自己創(chuàng)建一個(gè)臨時(shí)對(duì)象用戶(hù)返回,那么這個(gè)臨時(shí)對(duì)象會(huì)消 耗一個(gè)構(gòu)造函數(shù)(Constructor)的調(diào)用、一個(gè)復(fù)制構(gòu)造函數(shù)的調(diào)用(Copy Constructor)以及一個(gè)析構(gòu)函數(shù)(Destructor)的調(diào)用的代價(jià)。而如果稍微做一點(diǎn)優(yōu)化,就可以將成本降低到一個(gè)構(gòu)造函數(shù)的代價(jià),下面是 在Visual Studio 2008的Debug模式下做的一個(gè)測(cè)試:(在GCC下測(cè)試的時(shí)候可能編譯器自己進(jìn)行了RVO優(yōu)化,看不到兩種代碼的區(qū)別)
// C++ Return Value Optimization
// 作者:代碼瘋子
// 博客: http://www.programlife.net/
#include
using namespace std;
class Rational
{
public:
Rational( int numerator = 0, int denominator = 1) : n(numerator), d(denominator) {
cout << " Constructor Called... " << endl;
}
~Rational() {
cout << " Destructor Called... " << endl;
}
Rational( const Rational& rhs) {
this->d = rhs.d;
this->n = rhs.n;
cout << " Copy Constructor Called... " << endl;
}
int numerator() const { return n; }
int denominator() const { return d; }
private:
int n, d;
};
const Rational operator*( const Rational& lhs, const Rational& rhs) {
cout << " ----------- Enter operator* ----------- " << endl;
Rational tmp(lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denominator());
cout << " ----------- Leave operator* ----------- " << endl;
return tmp;
}
int main( int argc, char **argv) {
Rational x( 1, 5), y( 2, 9);
Rational z = x * y;
cout << " calc result: " << z.numerator()
<< " / " << z.denominator() << endl;
return 0;
}
函數(shù)輸出截圖如下:
可以看到消耗一個(gè)構(gòu)造函數(shù)(Constructor)的調(diào)用、一個(gè)復(fù)制構(gòu)造函數(shù)的調(diào)用(Copy Constructor)以及一個(gè)析構(gòu)函數(shù)(Destructor)的調(diào)用的代價(jià)。
而如果把operator*換成另一種形式:
const Rational operator*( const Rational& lhs, const Rational& rhs)
{
return Rational(lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denominator());
}
就只會(huì)消耗一個(gè)構(gòu)造函數(shù)的成本了:
參考: http://www.programlife.net/cpp-return-value-optimization.html
這是一項(xiàng)編譯器做的優(yōu)化,已經(jīng)是一種很常見(jiàn)的優(yōu)化手段了,搜一下可以找到很多的資料,在MSDN 里也有相關(guān)的說(shuō)明。
返回值優(yōu)化,顧名思義,就是與返回值有關(guān)的優(yōu)化,是當(dāng)函數(shù)是按值返回(而不是引用、指針)時(shí),為了避免產(chǎn)生不必要的臨時(shí)對(duì)象以及值拷貝而進(jìn)行的優(yōu)化。
先看看下面的代碼:
typedef unsigned
int UINT32;
class MyCla
{
public:
MyCla(UINT32 a_size =
10):size(a_size) {
p =
new UINT32[size];
}
MyCla(MyCla
const & a_right):size(a_right.size) {
p =
new UINT32[size];
memcpy(p, a_right.p, size*
sizeof(UINT32));
}
MyCla
const&
operator = (MyCla
const & a_right) {
size = a_right.size;
p =
new UINT32[size];
memcpy(p, a_right.p, size*
sizeof(UINT32));
return *
this;
}
~MyCla() {
delete [] p;
}
private:
UINT32 *p;
UINT32 size;
};
MyCla TestFun() {
return MyCla();
}
int _tmain(
int argc, _TCHAR* argv[])
{
MyCla a = TestFun();
return
0;
}
TestFun() 函數(shù)返回了一個(gè) MyCla 對(duì)象,而且是按值傳遞的。
在沒(méi)有任何“優(yōu)化”之前,這段代碼的行為也許是這樣的:return MyCla() 這行代碼中,構(gòu)造了一個(gè) MyCla 類(lèi)的臨時(shí)的無(wú)名對(duì)象(姑且叫它t1),接著把 t1 拷貝到另一塊臨時(shí)對(duì)象 t2(不在棧上),然后函數(shù)保存好 t2 的地址(放在 eax 寄存器中)后返回,TestFun 的棧區(qū)間被“撤消”(這時(shí) t1 也就“沒(méi)有”了,t1 的生存域在 TestFun 中,所以被析構(gòu)了),在 MyCla a = TestFun(); 這一句中,a 利用 t2 的地址,可以找到 t2 進(jìn)行,接著進(jìn)行構(gòu)造。這樣 a 的構(gòu)造過(guò)程就完成了。然后再把 t2 也“干掉”。
可以看到, 在這個(gè)過(guò)程中,t1 和 t2 這兩個(gè)臨時(shí)的對(duì)象的存在實(shí)在是很浪費(fèi)的,占用空間不說(shuō),關(guān)鍵是他們都只是為a的構(gòu)造而存在,a構(gòu)造完了之后生命也就終結(jié)了。既然這兩個(gè)臨時(shí)的對(duì)象對(duì)于程序 員來(lái)說(shuō)根本就“看不到、摸不著”(匿名對(duì)象),于是編譯器干脆在里面做點(diǎn)手腳,不生成它們!怎么做呢?很簡(jiǎn)單,編譯器“偷偷地”在我們寫(xiě)的TestFun 函數(shù)中增加一個(gè)參數(shù) MyCla&,然后把 a 的地址傳進(jìn)去(注意,這個(gè)時(shí)候 a 的內(nèi)存空間已經(jīng)存在了,但對(duì)象還沒(méi)有被“構(gòu)造”,也就是構(gòu)造函數(shù)還沒(méi)有被調(diào)用),然后在函數(shù)體內(nèi)部,直接用a來(lái)代替原來(lái)的“匿名對(duì)象”,在函數(shù)體內(nèi)部就完 成a的構(gòu)造。這樣,就省下了兩個(gè)臨時(shí)變量的開(kāi)銷(xiāo)。這就是所謂的“返回值優(yōu)化”!在 VC7 里,按值返回匿名對(duì)象時(shí),默認(rèn)都是這么做。
上 面說(shuō)的是“返回值優(yōu)化(RVO)”,還有一種“具名返回值優(yōu)化(NRVO)”,是對(duì)于按值返回“具名對(duì)象”(就是有名字的變量?。r(shí)的優(yōu)化手段,其實(shí)道理 是一樣的,但由于返回的值是具名變量,情況會(huì)復(fù)雜很多,所以,能執(zhí)行優(yōu)化的條件更苛刻,在下面三種情況下(來(lái)自 MSDN),NRVO 將一定不起作用:
不同的返回路徑上返回不同名的對(duì)象(比如if XXX 的時(shí)候返回x,else的時(shí)候返回y)
引入 EH 狀態(tài)的多個(gè)返回路徑(就算所有的路徑上返回的都是同一個(gè)具名對(duì)象)
在內(nèi)聯(lián)asm語(yǔ)句中引用了返回的對(duì)象名。
不過(guò)就算 NRVO 不能進(jìn)行,在上面的描述中的 t2 這個(gè)臨時(shí)變量也不會(huì)產(chǎn)生,對(duì)于 VC 的 C++ 編譯器來(lái)說(shuō),只要你寫(xiě)的程序是把對(duì)象按值返回的,它會(huì)有兩種做法,來(lái)避免 t2 的產(chǎn)生。拿下面這個(gè)程序來(lái)說(shuō)明:
MyCla TestFun2() {
MyCla x(
3);
return x;
}
一種做法是像 RVO一樣,把作為表達(dá)式中獲取返回值來(lái)進(jìn)行構(gòu)造的變量 a 當(dāng)成一個(gè)引用參數(shù)傳入函數(shù)中,然后在返回語(yǔ)句之前,用要返回的那個(gè)變量來(lái)拷貝構(gòu)造 a,然后再把這個(gè)變量析構(gòu),函數(shù)返回原調(diào)用點(diǎn),a 就構(gòu)造好了。
還有一種方式, 是在函數(shù)返回的時(shí)候,不析構(gòu)x,而直接把x的地址放到 exa 寄存器中,返回調(diào)到 TestFun2 的調(diào)用點(diǎn)上,這時(shí),a 可以用 exa 中存著的地址來(lái)進(jìn)行構(gòu)造,a 構(gòu)造完成之后,再析構(gòu)原來(lái)的變量 x !是的,注意到其實(shí)這時(shí),x 的生存域已經(jīng)超出了TestFun2,但由于這里x所在TestFun2的棧雖然已經(jīng)無(wú)效,但是并沒(méi)有誰(shuí)去擦寫(xiě)這塊存,所以x其實(shí)還是有效的,當(dāng)然,一切 都在匯編的層面,對(duì)于C++語(yǔ)言層面來(lái)講是透明的。
“c++臨時(shí)對(duì)象是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!