最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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á)式的時候,如果需要一個地方存儲其運(yùn)算結(jié)果,編譯器會創(chuàng)建一個沒命名的對象,這就是臨時對象。C++程序員通常用temporary這個術(shù)語來代替temporary object。

用函數(shù)返回值初始化臨時對象與用實(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ù)棧中的臨時變量不再有意義。

但是這么做通常也不會造成什么副作用,因?yàn)樵诤瘮?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í)是左值引用,而一個右值對象是不能夠綁定到一個左值引用的,所以編譯器會報錯。

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

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

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

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

但是對它的修改是沒有什么意義的,本來函數(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í)意圖,函數(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)用的時機(jī),更多關(guān)于右值,右值引用和移動構(gòu)造函數(shù)詳細(xì)的研究,待續(xù)。

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

相關(guān)文章

  • Qt實(shí)現(xiàn)屏幕底部冒泡效果

    Qt實(shí)現(xiàn)屏幕底部冒泡效果

    這篇文章主要為大家詳細(xì)介紹了Qt實(shí)現(xiàn)屏幕底部冒泡效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-08-08
  • 求素數(shù),用vector存儲的實(shí)現(xiàn)方法

    求素數(shù),用vector存儲的實(shí)現(xiàn)方法

    本篇文章是對求素數(shù),用vector存儲的實(shí)現(xiàn)方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • C語言商品銷售系統(tǒng)源碼分享

    C語言商品銷售系統(tǒng)源碼分享

    這篇文章主要為大家分享了C語言商品銷售系統(tǒng)源碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-02-02
  • C語言FlappyBird飛揚(yáng)的小鳥實(shí)現(xiàn)開發(fā)流程

    C語言FlappyBird飛揚(yáng)的小鳥實(shí)現(xiàn)開發(fā)流程

    因?yàn)樵诩艺撕枚嗵?,隨手玩了下自己以前做的一些小游戲,說真的,有幾個游戲做的是真的劣質(zhì),譬如 flappybird 真的讓我難以忍受,于是重做了一波分享給大家
    2022-11-11
  • Qt使用隨機(jī)驗(yàn)證碼的實(shí)現(xiàn)示例

    Qt使用隨機(jī)驗(yàn)證碼的實(shí)現(xiàn)示例

    有時候在登錄界面需要驗(yàn)證碼功能,這樣能夠防止被惡意程序攻擊,本文主要介紹了Qt使用隨機(jī)驗(yàn)證碼的實(shí)現(xiàn)示例,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • C語言實(shí)現(xiàn)歌手比賽系統(tǒng)

    C語言實(shí)現(xiàn)歌手比賽系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)歌手比賽系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • C++淺析引用的定義與使用

    C++淺析引用的定義與使用

    引用是C++一個很重要的特性,顧名思義是某一個變量或?qū)ο蟮膭e名,對引用的操作與對其所綁定的變量或?qū)ο蟮牟僮魍耆葍r,這篇文章主要給大家總結(jié)介紹了C++中引用的相關(guān)知識點(diǎn),需要的朋友可以參考下
    2022-07-07
  • C語言中的直接插入排序(帶圖詳細(xì))

    C語言中的直接插入排序(帶圖詳細(xì))

    這篇文章主要介紹了C語言中的直接插入排序(帶圖詳細(xì)),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • 數(shù)據(jù)結(jié)構(gòu)之伸展樹詳解

    數(shù)據(jù)結(jié)構(gòu)之伸展樹詳解

    這篇文章主要介紹了數(shù)據(jù)結(jié)構(gòu)之伸展樹詳解,本文對伸展樹(Splay Tree)的單旋轉(zhuǎn)操作、一字型旋轉(zhuǎn)、之字形旋轉(zhuǎn)區(qū)間操作等理論知識做了講解,并給出實(shí)現(xiàn)代碼,需要的朋友可以參考下
    2014-08-08
  • MFC對話框中添加狀態(tài)欄的方法

    MFC對話框中添加狀態(tài)欄的方法

    這篇文章主要介紹了MFC對話框中添加狀態(tài)欄的方法,實(shí)例分析了MFC對話框添加狀態(tài)欄所涉及的相關(guān)成員變量與事件實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2015-07-07

最新評論

沂源县| 连江县| 岗巴县| 繁昌县| 光泽县| 年辖:市辖区| 北京市| 化德县| 肃宁县| 黄山市| 秭归县| 长兴县| 盐山县| 项城市| 舒城县| 扎兰屯市| 建水县| 贵州省| 淄博市| 泰来县| 定结县| 南昌县| 延吉市| 台北县| 内乡县| 通化县| 丹东市| 绵阳市| 五寨县| 大竹县| 岱山县| 尼木县| 镶黄旗| 高阳县| 瑞昌市| 沛县| 大足县| 怀安县| 论坛| 临邑县| 永胜县|