C++中的函數(shù)返回值與拷貝用法
C++函數(shù)返回值與拷貝
先來(lái)談?wù)剬?duì)C++中函數(shù)返回return的理解,自己本來(lái)在學(xué)Java,但是平時(shí)學(xué)校的項(xiàng)目是用的C++,所以在平時(shí)搬磚時(shí)經(jīng)常會(huì)有一些問(wèn)題,今天就來(lái)談?wù)勄岸螘r(shí)間注意到的一個(gè)很小的知識(shí)點(diǎn),話不多說(shuō),先上列子。
首先我們創(chuàng)建一個(gè)簡(jiǎn)單的Man類,實(shí)現(xiàn)它的無(wú)參構(gòu)造函數(shù)、有參構(gòu)造函數(shù)和析構(gòu)函數(shù):
class Man { public: Man() { cout << "構(gòu)造" << endl; data = new int(0); } Man(const Man& m) { cout << "拷貝構(gòu)造" << endl; this->data = m.data; } ~Man() { cout << "析構(gòu)" << endl; delete data; } int* data; };
聲明一個(gè)get函數(shù)獲取一個(gè)Man的對(duì)象
Man get(Man& m) { cout << "----" << endl; return m; }
在main函數(shù)中執(zhí)行下列代碼
void main() { Man m, n; //cout << "before m=" << &m << "n=" << &n << endl; *m.data = 5; printf("m.data is %d\n", *m.data); n = get(m); printf("m.data is %d\n", *m.data); printf("n.data is %d\n", *n.data); system("pause"); }
你可以試著想一想三個(gè)printf的輸出結(jié)果分別是多少
執(zhí)行結(jié)果如下圖所示:
在輸出結(jié)果里我們可以清楚的看到,Man m, n; 創(chuàng)建了m,n兩個(gè)對(duì)象,調(diào)用了構(gòu)造函數(shù),對(duì)m對(duì)象中的data賦值,然后我們調(diào)用get(Man& man) 函數(shù),注意這里函數(shù)參數(shù)是引用類型,因此傳入的對(duì)象是m對(duì)象本身,這里我們要區(qū)別get(Man man) 兩種函數(shù)參數(shù)類型的區(qū)別,我稍后再提。get(Man& man) 函數(shù)調(diào)用完畢后,返回對(duì)象m。
按照我們過(guò)去的分析會(huì)認(rèn)為對(duì)象n等于get函數(shù)返回的m對(duì)象 (n=m) (注意這里等號(hào)=被重載過(guò)),m對(duì)象中的int* data 成員值直接賦值給了n對(duì)象中的data成員,輸出時(shí)照理說(shuō)m和n的data值都應(yīng)該等于5的,但是:
為什么這里輸出結(jié)果卻表明這個(gè)data指針指向的空間被銷毀了?
為什么get函數(shù)執(zhí)行里會(huì)多出了拷貝構(gòu)造和析構(gòu)這兩個(gè)過(guò)程呢?
如果我們返回值為Man&會(huì)有什么區(qū)別變化呢?
這里我們做一個(gè)對(duì)比,填加一個(gè)getR函數(shù),返回值為Man& 引用類型:
Man& getR(Man& m) {? ? ? cout << "----" << endl; ?? ?return m; }
接下來(lái)我們調(diào)用getR這個(gè)函數(shù)看一看輸出結(jié)果:
void main() { ?? ??? ?Man m, n; ?? ??? ?*m.data = 5; ?? ??? ?printf("m.data is %d\n", *m.data); ?? ??? ?n = getR(m); ?? ??? ? ?? ??? ?printf("m.data is %d\n", *m.data); ?? ??? ?printf("n.data is %d\n", *n.data); ?? ??? ? ?? ? ? ?system("pause"); ?? ? ? ?}
執(zhí)行結(jié)果如下圖所示:
執(zhí)行結(jié)果如下圖所示:
可以看到,當(dāng)我們返回的是m對(duì)象的的引用時(shí),getR 函數(shù)執(zhí)行時(shí)沒(méi)有調(diào)用拷貝構(gòu)造和析構(gòu)函數(shù)
這里我解釋一下返回值不是引用的情況時(shí)整個(gè)函數(shù)執(zhí)行的過(guò)程
(個(gè)人拙劣的理解)
我們?cè)倩氐絞et這個(gè)函數(shù):
Man get(Man& m) { cout << "----" << endl; return m; }
首先函數(shù)參數(shù)傳入m這個(gè)對(duì)象的引用我們毋庸置疑,關(guān)鍵就在return這里。
我們捋一捋函數(shù)從開始到結(jié)束這個(gè)過(guò)程,隨著Main函數(shù)調(diào)用get函數(shù),get函數(shù)入棧,同時(shí)get方法對(duì)應(yīng)的棧幀(儲(chǔ)存函數(shù)局部變量、返回地址等信息)也入棧,這里的局部變量也就是m對(duì)象的引用。
當(dāng)我們r(jià)eturn這個(gè)m對(duì)象時(shí),會(huì)在內(nèi)存中創(chuàng)建一個(gè)臨時(shí)的Man temp對(duì)象,同時(shí)這個(gè)temp對(duì)象調(diào)用其拷貝構(gòu)造函數(shù),也就是Man temp(m) 。
完成temp對(duì)象的創(chuàng)建后,get函數(shù)出棧,對(duì)應(yīng)的棧區(qū)內(nèi)容被銷毀,這時(shí)系統(tǒng)會(huì)調(diào)用m對(duì)象的析構(gòu)函數(shù),注意這里有一個(gè)陷阱!?。?!
由于m對(duì)象是在main方法下的棧區(qū)創(chuàng)建的,因此get方法出棧后,系統(tǒng)調(diào)用m析構(gòu)函數(shù)并沒(méi)有真正把m對(duì)象在棧區(qū)銷毀(因?yàn)樗揪筒皇窃趃et方法的棧區(qū)上),調(diào)用析構(gòu)函數(shù)僅僅是將data指針?biāo)赶虻膬?nèi)存空間被銷毀了(delete data;),這也解釋了為什么m.data的值為-572662307。
當(dāng)main方法執(zhí)行完畢后,m對(duì)象才會(huì)調(diào)用析構(gòu)函數(shù)真正被銷毀,當(dāng)然,這也會(huì)帶來(lái)另一個(gè)問(wèn)題,data指向的內(nèi)存區(qū)被執(zhí)行了兩次delete,運(yùn)行結(jié)束后你也就會(huì)發(fā)現(xiàn)還會(huì)有一個(gè)**“析構(gòu)”**和一個(gè)內(nèi)存問(wèn)題報(bào)錯(cuò)。
回到我們返回的值上:
n = get(m);
這里實(shí)際上可以理解成
Man temp(m); n=temp;
當(dāng)然由于get函數(shù)的退出調(diào)用析構(gòu)函數(shù)時(shí),data指針指向的內(nèi)存區(qū)域數(shù)據(jù)已經(jīng)被銷毀,自然n和m得到的值是一個(gè)錯(cuò)誤值了。
總結(jié)
對(duì)于函數(shù)返回值類型為非引用類型(當(dāng)然引用類型也可以理解為Man& temp=m),都是會(huì)在內(nèi)存中創(chuàng)建一個(gè)臨時(shí)變量,將返回值拷貝到臨時(shí)變量中,而返回值是作為函數(shù)調(diào)用棧區(qū)中的局部變量,隨著函數(shù)的返回,棧區(qū)的銷毀,而被銷毀。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
C++如何計(jì)算結(jié)構(gòu)體與對(duì)象的大小
這篇文章主要給大家介紹了關(guān)于C++如何計(jì)算結(jié)構(gòu)體與對(duì)象大小的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05linux c語(yǔ)言操作數(shù)據(jù)庫(kù)(連接sqlite數(shù)據(jù)庫(kù))
linux下c語(yǔ)言操作sqlite數(shù)據(jù)庫(kù)實(shí)例方法,大家參考使用吧2013-12-12Qt學(xué)習(xí)之QListWidget控件的使用教程詳解
這篇文章主要為大家詳細(xì)介紹了Qt中QListWidget控件的使用教程,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Qt有一定的幫助,需要的可以參考一下2022-12-12