C++
和
C#/java
有很多區(qū)別,其中最大的區(qū)別當(dāng)數(shù)對(duì)內(nèi)存的管理。
C++
中,類的使用者決定了類的實(shí)例內(nèi)存會(huì)如何分配,分配在堆上還是棧上。我們先看一段例子程序:
?
#include
"stdio.h"
?
class
Demo{
public
:
???
int i;
???
char* objName;
??? Demo(){
??????? objName = "Default object.";
??????? printf("%s, objName = %s\r\n", "Enter Demo default ctor. method.", objName);
???????
??????? i = 1000;
??? }
?
??? Demo(int ival, char* name){
??????? printf("%s,i = %d, objName = %s\r\n", "Enter Demo(int ival) ctor method", ival, name);
??????? i = ival;
??????? objName = name;
??? }
?
??? Demo(const Demo& d){
??????? printf("%s\r\n", "Enter Demo copy ctor method.");
??????? i = d.i;
??????? objName = "copied d";
??? }
?
??? ~Demo(){
??????? printf("%s, i = %d, objName = %s\r\n", "Enter Demo dector. method" , i, objName);
??? }
};
?
Demo& testMethod0(){
??? printf("%s\r\n", "Enter testMethod0.");
??? Demo d(0, "d in testMethod0");
??? printf("%s\r\n", "Exit testMethod0.");
???
return d;
}
?
Demo testMethod1(){
??? printf("%s\r\n", "Enter testMethod1.");
??? Demo d(1, "d in testMethod1");
??? printf("%s\r\n", "Exit testMethod1.");
???
return d;
}
?
Demo* testMethod2(){
??? printf("%s\r\n", "Enter testMethod2.");
??? Demo *d = new Demo(2, "d in testMethod2");
??? printf("%s\r\n", "Exit testMethod2.");
???
return d;
}
?
int
main(int argc, _TCHAR* argv[])
{
??? Demo d;
??? d = testMethod1();
?
??? Demo& d1 = testMethod0();
?
??? Demo d2(999, "d1");
?
??? Demo* d3 = testMethod2();
?
??? printf("d.i = %d\r\n", d.i);
??? printf("d1.i = %d\r\n", d1.i);
??? printf("d2.i = %d\r\n", d2.i);
??? printf("d3.i = %d\r\n", d3->i);
?
???
delete d3;
???
return 0;
}
?
Output
:
Enter Demo default ctor. method., objName = Default object.
Enter testMethod1.
Enter Demo(int ival) ctor method,i = 1, objName = d in testMethod1
Exit testMethod1.
Enter Demo copy ctor method.
Enter Demo dector. method, i = 1, objName = d in testMethod1
Enter Demo dector. method, i = 1, objName = copied d
Enter testMethod0.
Enter Demo(int ival) ctor method,i = 0, objName = d in testMethod0
Exit testMethod0.
Enter Demo dector. method, i = 0, objName = d in testMethod0
Enter Demo(int ival) ctor method,i = 999, objName = d1
Enter testMethod2
Enter Demo(int ival) ctor method,i = 2, objName = d in testMethod2
Exit testMethod2.
d.i = 1
d1.i = -2
d2.i = 999
d3.i = 2
Enter Demo dector. method, i = 2, objName = d in testMethod2
Enter Demo dector. method, i = 999, objName = d1
Enter Demo dector. method, i = 1, objName = copied d
?
與
C#
不同,在
C++
中,對(duì)象聲明的時(shí)候就已經(jīng)執(zhí)行了構(gòu)造函數(shù),比如上面例子的
main
函數(shù)中的第一行,
Demo d
,從屏幕上的輸出來看,這個(gè)時(shí)候
Demo class
的默認(rèn)構(gòu)造函數(shù)會(huì)被調(diào)用。
接下來的一行代碼調(diào)用,引出了很有趣的情況,當(dāng)然也隱藏著不小的問題。這行代碼造成了一次構(gòu)造函數(shù)調(diào)用,一次拷貝構(gòu)造函數(shù)調(diào)用和兩次析構(gòu)函數(shù)調(diào)用。讓我們來具體分析一下:第一次調(diào)用構(gòu)造函數(shù)很容易理解,因?yàn)樵?/span>
testMethod1
中我們聲明了
Demo d(0)
,退出
testMethod1
,函數(shù)的返回值要賦值給變量
d2
,這個(gè)時(shí)候,
d2
被拷貝構(gòu)造函數(shù)重新構(gòu)造了一次。接著
testMethod1
中構(gòu)造的局部變量被析構(gòu),然后,居然拷貝構(gòu)造函數(shù)構(gòu)造的對(duì)象也被析構(gòu)?等等,看完所有輸出,我們發(fā)現(xiàn),
objName = copied d
的對(duì)象被析構(gòu)兩次,而
objName = Default obj
的對(duì)象被構(gòu)造出之后沒有被析構(gòu),這里隱藏了很嚴(yán)重的問題,有可能導(dǎo)致內(nèi)存泄漏、句柄不能被正確關(guān)閉等等。另外,拷貝構(gòu)造函數(shù)的執(zhí)行可能導(dǎo)致潛在的效率問題,考慮一個(gè)包含巨大矩陣的對(duì)象,
copy
這個(gè)對(duì)象會(huì)怎么樣?
?
接下來的一行代碼,
testMethod0
返回一個(gè)對(duì)象的引用,當(dāng)然不會(huì)導(dǎo)致拷貝構(gòu)造函數(shù)被調(diào)用,但是,這樣也是有問題的,在函數(shù)中聲明的局部變量在函數(shù)執(zhí)行完成的時(shí)候會(huì)被析構(gòu),那么直接返回局部變量就可能會(huì)出現(xiàn)問題。
testMethod0
退出以后,他內(nèi)部的
Demo
對(duì)象就會(huì)自動(dòng)析構(gòu),外面對(duì)它的引用當(dāng)然也無法指向正確的對(duì)象了,所以后面程序打印
d.i
的時(shí)候,輸出了一個(gè)莫名其妙的
-2
。
?
效率最好的方法當(dāng)數(shù)返回指針了,它不會(huì)導(dǎo)致對(duì)象復(fù)制,如果使用得當(dāng),也不會(huì)導(dǎo)致內(nèi)存泄漏或者句柄泄漏。
testMethod2
演示了這種情況,當(dāng)然,你需要手工刪除在
testMethod2
中創(chuàng)建的對(duì)象。
?
?