經(jīng)常看到這樣的聲明:T& func(T& t),這種聲明和T func(T t)有什么區(qū)別?書上的解釋是為了提高效率,究竟是如何提高效率的呢?內(nèi)部執(zhí)行了什么操作?本文通過8個小例子對引用參數(shù)和引用返回進(jìn)行了一次徹底的排查。
首先看一下在類的成員函數(shù)中的引用參數(shù)和引用返回值:
這個類很簡單,只有一個成員變量x,并且定義了默認(rèn)構(gòu)造函數(shù)、拷貝構(gòu)造函數(shù)、析構(gòu)函數(shù)和賦值函數(shù)。為了能夠更清楚地看到哪個拷貝構(gòu)造函數(shù)與賦值函數(shù)是否被調(diào)用,在這兩個函數(shù)中添加了一些輸出信息。
類中還定義了四個成員函數(shù),下面分別分析這四個函數(shù)的執(zhí)行情況。
(1) 在main()函數(shù)中調(diào)用func1():
(2) 在main()函數(shù)中調(diào)用func2()以與func1()對比:
結(jié)果什么也沒有輸出。
這是由于傳入的是一個引用參數(shù),因此在函數(shù)內(nèi)部不需要產(chǎn)生一個臨時對象來保存對象信息,因此不會調(diào)用拷貝構(gòu)造函數(shù)。這就是引用參數(shù)的作用,減少一次對象的拷貝,提高了函數(shù)的效率。
(3) 在main()函數(shù)中調(diào)用func3():
為什么會輸出"Copy"呢?這是因?yàn)楹瘮?shù)采用的是值返回,因此為了保存返回值,需要先創(chuàng)建一個臨時對象,然后調(diào)用類的拷貝構(gòu)造函數(shù)將*this的內(nèi)容拷貝到這個臨時對象中,再將臨時對象返回。最后通過賦值函數(shù)將該臨時對象的內(nèi)容賦值給新對象。
(4) 在main()函數(shù)中調(diào)用func4()以與func3()對比:
只調(diào)用了賦值函數(shù),這是引用函數(shù)采用的是引用返回,因此直接返回對象自身的引用*this,不需要創(chuàng)建臨時對象來保存對象信息,因此不會調(diào)用拷貝構(gòu)造函數(shù)。最后通過賦值函數(shù)直接將對象本身的內(nèi)容賦值給新對象。這就是引用返回值的作用,減少了一次對象的拷貝,提高了函數(shù)的效率。
總結(jié)一下:在類的成員函數(shù)中,使用引用參數(shù)和引用返回值都不需要產(chǎn)生臨時對象,減少了一次對象的拷貝,提高了函數(shù)的效率。
那么,如果將參數(shù)作為返回值返回,并且用引用接收返回值將會產(chǎn)生什么效果呢?下面定義四個全局函數(shù):
(5) 在main()函數(shù)中調(diào)用func5():
func5()采用了引用參數(shù),并且以引用返回值的方式返回了該參數(shù),因此a2是a1的一個引用,對a1的任何改變都會反映到a2上,所以a1、a2的成員變量x的值相同。
(6) 在main()函數(shù)中調(diào)用func6():
編譯的時候會報一個警告:
警告的意思就是返回了一個局部變量的引用,這種用法實(shí)際上是錯誤的。局部變量在函數(shù)返回前就會被釋放,因此實(shí)際上a2引用到的一塊不可知的內(nèi)存,這從輸出的a2.x的值"4198610"也可以看出來。至于輸出"Copy",是因?yàn)椴捎玫氖侵祬?shù),上面已經(jīng)討論過,這里不再贅述。
(7) 在main()函數(shù)中調(diào)用func7():
這是一種比較特殊的用法,由于func7()采用的是值返回,因此在函數(shù)返回前將會產(chǎn)生一個臨時對象,并執(zhí)行一次拷貝構(gòu)造函數(shù)。這樣相當(dāng)于a2引用了一個臨時對象。前面曾經(jīng)說過,臨時對象將會在函數(shù)返回前被釋放,但是為什么這里輸出的結(jié)果是正常的呢?這是一種特殊情況,C++規(guī)定,如果有臨時對象有一個引用,那么這個臨時對象的生存期將延長到和這個引用相同。這樣就可以解釋上面的輸出結(jié)果了:a2引用了一個臨時對象,而不是引用了a1,因此a1的任何改變不會影響到a2。
注意:在VC編譯環(huán)境下,const A& a2 = func7(a1);這行語句前面可以不加"const",但是在g++或者其他版本的編譯器中不加"const"將會產(chǎn)生編譯錯誤。加上"const"更加符合C++標(biāo)準(zhǔn)的規(guī)定,因?yàn)榕R時對象不可見,不允許通過該引用來改變臨時對象的內(nèi)容。
(8) 在main()函數(shù)中調(diào)用func8():
通過以上的分析,對這個輸出結(jié)果也就很好理解了:由于采用的是值參數(shù),因此在函數(shù)體執(zhí)行前會調(diào)用一次拷貝構(gòu)造函數(shù);采用的是值返回值,因此在函數(shù)返回前又會調(diào)用一次拷貝構(gòu)造函數(shù),這就是前兩個"Copy"的由來。另外,a2引用的是一個臨時對象,而不是引用了a1,因此a1的任何改變不會影響到a2。
總結(jié)一下:
如果使用引用接收引用返回值,則返回的引用必須具有較長的生存期,不可以引用局部變量。
如果使用引用接收值返回值,則引用了一個臨時對象,該對象的生存期將延長到和這個引用相同。
原文出處:http://www.cnblogs.com/bigshow/archive/2008/11/10/1330514.html