欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++返回值是類名和返回值是引用的區(qū)別及說明

 更新時間:2022年11月25日 09:49:51   作者:四月晴  
這篇文章主要介紹了C++返回值是類名和返回值是引用的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

返回值是類名和返回值是引用的區(qū)別

返回非引用類型

函數(shù)的返回值用于初始化在調(diào)用函數(shù)時創(chuàng)建的臨時對象(temporary object),如果返回類型不是引用,在調(diào)用函數(shù)的地方會將函數(shù)返回值復(fù)制給臨時對象。

在求解表達(dá)式的時候,如果需要一個地方存儲其運算結(jié)果,編譯器會創(chuàng)建一個沒命名的對象,這就是臨時對象。C++程序員通常用temporary這個術(shù)語來代替temporary object。

用函數(shù)返回值初始化臨時對象與用實參初始化形參的方法是一樣的。

當(dāng)函數(shù)返回非引用類型時,其返回值既可以是局部對象,也可以是求解表達(dá)式的結(jié)果。

返回引用類型

當(dāng)函數(shù)返回引用類型時,沒有復(fù)制返回值,相反,返回的是對象本身。

千萬不要返回局部對象的引用!千萬不要返回指向局部對象的指針!

當(dāng)函數(shù)執(zhí)行完畢時,將釋放分配給局部對象的存儲空間。此時對局部對象的引用就會指向不確定的內(nèi)存!返回指向局部對象的指針也是一樣的,當(dāng)函數(shù)結(jié)束時,局部對象被釋放,返回的指針就變成了不再存在的對象的懸垂指針。

返回引用時,要求在函數(shù)的參數(shù)中,包含有以引用方式或指針方式存在的,需要被返回的參數(shù)。

如果返回對象,最后多執(zhí)行一次拷貝構(gòu)造函數(shù),如果返回引用,直接返回現(xiàn)存對象

#include <iostream>
using namespace std;

class Timer
{
public:
? ? Timer();
? ? Timer(int, int, int);
? ? friend Timer ?&operator+(Timer&, Timer&);
? ? friend Timer operator-(Timer&, Timer&);
? ? friend ostream& operator<<(ostream &out, Timer &t);
? ? friend istream& operator>>(istream &in, Timer &t);
private:
? ? int hour, minute, second;
};

Timer::Timer()
{
? ? hour = 0;
? ? minute = 0;
? ? second = 0;
}

Timer::Timer(int hour, int minute, int second)
{
? ? this->hour = hour;
? ? this->minute = minute;
? ? this->second = second;
}

Timer & operator+(Timer& a, Timer &b)
{
? ? a.second = a.second + b.second;
? ? a.minute = a.minute + b.minute;
? ? a.hour = a.hour + b.hour;
? ? if (a.second >= 60)
? ? {
? ? ? ? a.second = a.second - 60;
? ? ? ? a.minute++;
? ? }
? ? if (a.minute >= 60)
? ? {
? ? ? ? a.minute = a.minute - 60;
? ? ? ? a.hour++;
? ? }
? ? return a;
}

Timer operator-(Timer &a, Timer &b)
{
? ? Timer c;
? ? c.hour = a.hour - b.hour;
? ? c.minute = a.minute - b.minute;
? ? c.second = a.second - b.second;
? ? if (c.second < 0)
? ? {
? ? ? ? c.second += 60;
? ? ? ? c.minute--;
? ? }
? ? if (c.minute < 0)
? ? {
? ? ? ? c.minute += 60;
? ? ? ? c.hour--;
? ? }
? ? return c;
}

ostream& operator<<(ostream &out, Timer &t)
{
? ? out << t.hour << ":" << t.minute << ":" << t.second << endl;
? ? return out;
}

istream& operator>>(istream&in, Timer &t)
{
? ? cout << "Input hours and minutes." << endl;
? ? in >> t.hour >> t.minute >> t.second;
? ? return in;
}

int main()
{
? ? Timer t1, t2, t3, t4;
? ? cin >> t1 >> t2;
? ? cout << "t1=" << t1;
? ? cout << "t2=" << t2;
? ? t3 = t1 + t2;
? ? cout << "t3=t1+t2=" << t3;
? ? t4 = t1 - t2;
? ? cout << "t4=t1-t2=" << t4;
? ? return 0;
}

剛開始我將函數(shù)聲明為:

friend Timer&operator+(Timer&,Timer&);
friend Timer&operator-(Timer&,Timer&);

對于<<,>>的重載聲明的和長代碼一樣。但是這樣做之后我發(fā)現(xiàn)出現(xiàn)異常。

在Time &operator+(Timer&t1,Timer&t2)里我聲明了一個Timer的局部變量,而當(dāng)函數(shù)調(diào)用結(jié)束后,該指針變成了懸掛指針。

所以,一定謹(jǐn)記不要在返回值為引用的函數(shù)中返回局部變量。

C++函數(shù)返回值和返回引用問題

C++函數(shù)的返回過程基本可以分為兩個階段,返回階段和綁定階段,根據(jù)兩個階段中需要返回的值的類型不同(返回值和引用),和要綁定的值的類型(綁定值和引用)會產(chǎn)生不同的情況。

最基本的規(guī)則是先返回,再綁定,返回和綁定的時候,都有可能發(fā)生移動或者拷貝構(gòu)造函數(shù)的調(diào)用來創(chuàng)建臨時對象,并且只會發(fā)生一次。更具體的,當(dāng)返回值的時候,函數(shù)返回之前,會調(diào)用一次拷貝構(gòu)造函數(shù)或移動構(gòu)造函數(shù),函數(shù)在綁定到值的時候,會發(fā)生一次拷貝構(gòu)造函數(shù)或移動構(gòu)造函數(shù),如果返回時已經(jīng)調(diào)用了構(gòu)造函數(shù),則綁定時不會再調(diào)用,而是直接綁定。返回引用或者綁定到引用,在各自階段,不會產(chǎn)生構(gòu)造函數(shù)的調(diào)用。

下面舉例闡述各種可能的情況(例子中很多情況我們是不該這樣寫的,本文只討論如果這樣做了會怎樣)。

關(guān)于右值引用和移動構(gòu)造函數(shù)的解釋,后面會寫篇東西做個總結(jié)。這里僅需要一條規(guī)則,那就是在需要調(diào)用移動構(gòu)造函數(shù)的情況,如果類沒有定義移動構(gòu)造函數(shù),則調(diào)用它的拷貝構(gòu)造函數(shù),后面對未定義移動構(gòu)造函數(shù)的情況,不再贅述。

為敘述方便,定義綁定值和返回值:

綁定值就是函數(shù)賦給的變量,返回值就是函數(shù)的返回值,最終的調(diào)用形式類似:

返回值 fun()
{
?? ?.....
?? ?return 返回值;
}

綁定值 = func();

定義一個類:

class myClass
{
public:
? ? //構(gòu)造函數(shù)
?? ?myClass()
?? ?{
?? ??? ?cout << "construct" << endl;
?? ?}
? ? //拷貝構(gòu)造函數(shù)
?? ?myClass(const myClass& rhs)
?? ?{
?? ??? ?cout << "copy construct" << endl;
?? ?}
? ? //移動構(gòu)造函數(shù)
?? ?myClass(myClass&& rhs)
?? ?{
?? ??? ?cout << "move consturct" << endl;
?? ?}
? ? //析構(gòu)函數(shù)
?? ?~myClass()
?? ?{
?? ??? ?cout << "desctruct" << endl;
?? ?}
};

1.綁定值類型為值類型

返回值類型為值類型,返回的是局部變量:

在函數(shù)返回階段,調(diào)用類的移動構(gòu)造函數(shù)創(chuàng)建返回值,并綁定到綁定值上。移動構(gòu)造函數(shù)的調(diào)用發(fā)生在函數(shù)返回階段。

例子:

myClass fun()
{
?? ?myClass mc;
?? ?return mc;
}
?
int main()
{
?? ?myClass result = fun();
}

輸出:

construct
move consturct
desctruct
desctruct

2.綁定值類型為值類型

返回值類型為值類型,返回的是局部變量的引用:

首先,這不是一個好的做法,企圖返回一個局部變量的引用結(jié)果通常并不會令人滿意。

函數(shù)調(diào)用結(jié)束后,函數(shù)幀被回收(實際上只是設(shè)置了棧頂?shù)牡刂罚?,函?shù)棧中的臨時變量不再有意義。

但是這么做通常也不會造成什么副作用,因為在函數(shù)返回階段,會調(diào)用拷貝構(gòu)造函數(shù)(注意,即使定義了移動構(gòu)造函數(shù)也不會調(diào)用),將生成的新的臨時對象,綁定到綁定值上。

例子:

myClass fun()
{
?? ?myClass mc;
?? ?myClass& mcRef = mc;
? ? ? ? //如果是想返回這個局部變量的引用,并且在函數(shù)外部對其進(jìn)行修改,那必然要失望了。
?? ?return mcRef;
}
?
int main()
{
?? ?myClass result = fun();
}

輸出:

construct
copy construct
desctruct
desctruct

3.綁定值類型為值類型

返回值類型為值類型,返回的是全局變量的引用:

和2中的情形類似,但是由于全局變量的生命周期超過當(dāng)前函數(shù),所以即使函數(shù)返回,變量仍然存活。在函數(shù)返回階段,仍然會調(diào)用拷貝構(gòu)造函數(shù),將產(chǎn)生得臨時對象綁定到綁定值上,卻依然不能返回全局對象。

例子:

通常,返回作為參數(shù)而傳遞進(jìn)來的引用的情況更加常見,此時的參數(shù)對于函數(shù)來說,和全局變量類似,即函數(shù)返回,變量的生命周期也不會結(jié)束。

myClass fun(myClass& param)
{
? ? ? ? //最終只能得到param的拷貝
?? ?return param;
}
?
int main()
{
?? ?myClass mc;
?? ?myClass result = fun(mc);
}

輸出:

construct
copy construct
desctruct
desctruct

4.綁定值類型為值類型

返回值類型為引用類型,返回的是局部變量或局部變量的引用:

和情況2情形類似的是,希望以引用的方式返回局部變量或者局部變量的引用,通常也不能取得令人滿意的結(jié)果;但與情況2的情形不同的是,情況2基本不會產(chǎn)生什么副作用,情況2中在函數(shù)返回階段對對象進(jìn)行拷貝,此時對象完整;但是在本情況中,就會產(chǎn)生嚴(yán)重的副作用。

由于返回值類型為引用類型,在函數(shù)返回階段,并不會調(diào)用拷貝構(gòu)造函數(shù),而在綁定階段,綁定值是值類型,才會產(chǎn)生拷貝構(gòu)造函數(shù)的調(diào)用,而此時函數(shù)已經(jīng)返回,拷貝函數(shù)拷貝的是函數(shù)中的臨時變量,此時已經(jīng)析構(gòu)了,再進(jìn)行拷貝,只會得到一個被析構(gòu)對象的拷貝,通常,這會產(chǎn)生嚴(yán)重的錯誤。

簡單的說,這種情況下,會先析構(gòu)臨時對象,在拷貝這個臨時對象。

例子:

myClass& fun()
{
?? ?myClass mc;
?? ?//或者myClass mcRef = mc; return mcRef
?? ?return mc;
}
?
int main()
{
?? ?myClass result = fun();
}

輸出:

construct
desctruct
copy construct
desctruct

5.綁定值類型為值類型

返回值類型為引用類型,返回的是全局變量的引用:

基本的情況和2是類似的,即并不能真的的到全局變量的引用,但是也不會產(chǎn)生什么副作用(除非調(diào)用者就是希望或者這個全局變量,并進(jìn)行修改)。由于全局變量在函數(shù)結(jié)束之后并不會被析構(gòu),所以對它調(diào)用拷貝構(gòu)造函數(shù)是安全的。和情況4類似,拷貝函數(shù)的調(diào)用也不是發(fā)生在函數(shù)返回階段,而是發(fā)生在綁定階段。

例子:

同樣使用參數(shù)為引用類型的函數(shù)舉例:

myClass& fun(myClass& param)
{
?? ?return param;
}
?
int main()
{
?? ?myClass mc;
?? ?myClass result = fun(mc);
}

輸出:

construct
copy construct
desctruct
desctruct

上述5中情況是將返回值綁定到值類型的情形,所有情形中,均會出現(xiàn)拷貝或移動構(gòu)造函數(shù)的調(diào)用,最終綁定之后,都不可能得到原來的對象,企圖通過這種方式來修改原對象,必然會失敗。

如果綁定值類型為引用類型,返回值類型為值類型,返回的是局部變量,局部變量的引用或者全局變量會怎么樣呢?

這種情況編譯器會報錯,由于函數(shù)返回的是值類型,必然會產(chǎn)生移動構(gòu)造函數(shù)或者拷貝構(gòu)造函數(shù),產(chǎn)生一個臨時對象,而臨時對象是一個右值,我們常說的引用,其實是左值引用,而一個右值對象是不能夠綁定到一個左值引用的,所以編譯器會報錯。

我們可以將函數(shù)的返回值版綁定到右值引用變量(使用myClass&&),我們下面討論返回右值引用的情況。但是,有時候返回一個右值引用并且使用它,可能并不能產(chǎn)生想要的結(jié)果(如本文最后例子)。

6.綁定值類型為右值引用

返回值類型為值類型,返回的是局部變量或局部變量的引用或全局變量的引用:

函數(shù)返回階段,由于是返回值類型,所以會調(diào)用移動構(gòu)造函數(shù)或者拷貝構(gòu)造函數(shù),創(chuàng)建一個不具名的臨時變量(其實它就是一個右值),可以把它綁定到一個右值引用上。

但是對它的修改是沒有什么意義的,本來函數(shù)創(chuàng)建的這個不具名臨時變量,會在函數(shù)返回之后被析構(gòu),由于我們增加了一個指向它的引用,所以這個臨時變量的生命周期被延長了,直到指向它的引用離開作用域,它才會析構(gòu)。

例子:

myClass fun()
{
?? ?myClass mc;
?? ?return mc;
}
int main()
{
?? ?myClass&& result = func();
}

輸出:

construct
move consturct
desctruct
desctruct 

myClass fun()
{
?? ?myClass mc;
?? ?myClass& mcRef = mc;
?? ?return mcRef;
}
int main()
{
?? ?myClass&& result = fun();
}

輸出:

construct
copy construct
desctruct
desctruct

7.綁定值類型為為引用類型

返回值類型為引用類型,返回局部變量或局部變量的引用:

由于函數(shù)返回階段和綁定階段都是引用類型,所以不會產(chǎn)生任何拷貝或移動構(gòu)造函數(shù)的調(diào)用,最終綁定值類就是函數(shù)的局部變量,但是由于函數(shù)返回后,局部變量已經(jīng)被析構(gòu),在保存這個局部變量的引用沒有意義,往往會引起一些詭異的錯誤。

例子:

myClass& fun()
{
?? ?myClass mc;
?? ?//或者myClass& mcRef = mc; return mcRef;
?? ?return mc;
}
?
int main()
{
? ? ? ? //在函數(shù)返回之后,我們甚至還能使用這個result,但是僅僅是編譯器將原來result的位置繼續(xù)解釋成result變量,
? ? ? ? //很有可能很快這個位置會被新的棧幀覆蓋,繼續(xù)使用result,可能會出現(xiàn)“詭異”的問題
?? ?myClass& result = fun();
}

輸出:

construct
desctruct

8.綁定值類型為引用類型

返回值類型為引用類型,返回全局變量的引用:

通常,當(dāng)我們這是我們的真實意圖,函數(shù)在返回階段和綁定階段均不存在構(gòu)造函數(shù)的調(diào)用,最終綁定值就是需要返回的全局變量,對最終綁定值的任何修改,均會反映到全局變量上。

例子:

依然使用參數(shù)為引用類型的函數(shù)舉例。

myClass& fun(myClass& mc)
{
?? ?return mc;
}
int main()
{
?? ?myClass mc;
?? ?myClass& result = fun(mc);
}

輸出:

construct
desctruct

9.一個思考

右值是不能綁定到左值引用的,但是當(dāng)把右值付給左值會怎樣呢?

如下

int main()
{
?? ?myClass mc;
?? ?myClass result = move(mc);
}

答案是會調(diào)用移動構(gòu)造函數(shù)創(chuàng)建一個新的對象,并且綁定到綁定值。

輸出:

construct
move consturct
desctruct
desctruct

另一個非常類似的例子:

int main()
{
?? ?myClass mc;
?? ?myClass&& mcRightRef = move(mc);
?? ?myClass result = mcRightRef;
}

和第一個非常類似,但是result的生成是調(diào)用拷貝構(gòu)造函數(shù)而非移動構(gòu)造函數(shù)完成的。

輸出:

construct
copy construct
desctruct
desctruct

這就涉及到了移動構(gòu)造函數(shù)調(diào)用的時機,更多關(guān)于右值,右值引用和移動構(gòu)造函數(shù)詳細(xì)的研究,待續(xù)。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Qt Creator使用教程的簡單說明

    Qt Creator使用教程的簡單說明

    如今 Qt Creator 功能十分強大了,包含項目模板生成、代碼編輯、UI 設(shè)計、QML 界面編輯、調(diào)試程序、上下文幫助等豐富功能,本文就詳細(xì)的介紹一下如何使用
    2021-08-08
  • C語言深入詳解四大內(nèi)存函數(shù)的使用

    C語言深入詳解四大內(nèi)存函數(shù)的使用

    這篇文章主要介紹了C語言的四大內(nèi)存函數(shù),講解了mencpy、memmove、memcmp、memset函數(shù),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2022-07-07
  • 一起來學(xué)習(xí)C++中類的this指針以使用

    一起來學(xué)習(xí)C++中類的this指針以使用

    這篇文章主要為大家詳細(xì)介紹了C++中類的this指針以使用,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • 淺談socket TCP編程中connect的一些坑

    淺談socket TCP編程中connect的一些坑

    下面小編就為大家?guī)硪黄獪\談socket TCP編程中connect的一些坑。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-12-12
  • c語言數(shù)據(jù)結(jié)構(gòu)與算法之順序表的定義實現(xiàn)詳解

    c語言數(shù)據(jù)結(jié)構(gòu)與算法之順序表的定義實現(xiàn)詳解

    這篇文章主要介紹了c語言數(shù)據(jù)結(jié)構(gòu)與算法之順序表的定義實現(xiàn)詳解,用順序存儲的方式實現(xiàn)線性表順序存儲,把邏輯上相鄰的元素存儲在物理位置上也相鄰的存儲單元中,元素之間的關(guān)系由存儲單元的鄰接關(guān)系來體現(xiàn),需要的朋友可以參考下
    2023-08-08
  • C++11中的時間庫std::chrono(引發(fā)關(guān)于時間的思考)

    C++11中的時間庫std::chrono(引發(fā)關(guān)于時間的思考)

    這篇文章主要介紹了C++11中的時間庫std::chrono(引發(fā)關(guān)于時間的思考),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-04-04
  • C語言中的rand()和rand_r()詳解

    C語言中的rand()和rand_r()詳解

    這篇文章主要為大家介紹了C語言中的rand()和rand_r(),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2021-12-12
  • C++ Qt屬性系統(tǒng)詳細(xì)介紹

    C++ Qt屬性系統(tǒng)詳細(xì)介紹

    這篇文章主要介紹了C++ Qt屬性系統(tǒng)詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下
    2016-12-12
  • 簡單談?wù)凜++ 頭文件系列之(bitset)

    簡單談?wù)凜++ 頭文件系列之(bitset)

    我們要使用bitset,首先需要包含頭文件bitset。和vector對象不一樣的是bitset類型對象的區(qū)別僅在其長度而不在其類型。在定義bitset的時候,要明確bitset包含了多少位,須在尖括號內(nèi)給出它的長度值。長度值必須定義為整形字面值常量或是已用常量值初始化的整型的const對象
    2017-02-02
  • Opencv使用Stitcher類圖像拼接生成全景圖像

    Opencv使用Stitcher類圖像拼接生成全景圖像

    這篇文章主要為大家詳細(xì)介紹了Opencv使用Stitcher類圖像拼接生成全景圖像,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01

最新評論