標頭檔
class student
{
public:
student(char*);
~student();
student(const student &);
char* name;
static int num;
};
main.cpp檔案
int student::num=0;
student::student(char* myname)
{
num++;
int len=strlen(myname);
name=new char[len+1];
strcpy(name,myname);
cout<<name<<":建立,剩餘個數:"<<num<<endl;
}
student::~student()
{
num--;
cout<<name<<":銷毀,剩餘個數:"<<num<<endl;
delete [] name;
}
void fun(student funs)
{
cout<<funs.name<<"被調用"<<endl;
}
void main()
{
student stu1("student1");//--------------1
student stu2("student2");//--------------2
student stu3("student3");//--------------3
fun(stu2);//--------------4
student stu4=stu3;//--------------5
student stu5("student5");//--------------6
stu5=stu1;//--------------7
}
1,2,3都很正常,到4的時候,實參為類值對象,傳遞過程是一個拷貝建構函式,funs在fun方法結束後會調用解構函式,而運行到5的時候,這是一個通過拷貝建構函式執行個體化類,這個過程不會調用構造方法和解構函式的,因為我們要瞭解拷貝建構函式是如何寫的,如下
student(const student & c)
{
name=c.name;
}
所以5就相當於 student stu4(stu3)
到7的時候只是賦值,不是通過拷貝建構函式執行個體化類,因為stu5已經在6的時候執行個體化了
這個代碼在VC 6.0中啟動並執行時候,神奇般的出錯了,第一個錯誤出在main函數結束後,一個個析構,堆棧的析構順序是先進後出,先析構stu5,再析構stu4,再析構stu3,就在析構stu3的時候出錯了。如下
為什麼會出錯,此時研究後發現是因為在5這個位置調用了拷貝建構函式來構造stu4類對象,根據拷貝建構函式的原型(下是原型)
student(const student & c)
{
name=c.name;
}
我發現name=c.name,對於5來說就是stu4.name=stu3.name;name是一個指標,這是一個指標賦值,即stu4的name指標指向了stu3的那麼指標指向的位置。那麼問題迎刃而解了,即在析構stu4的時候(因為堆棧析構順序先析構晚壓入棧的),delete [] name了,就是告訴電腦,name這塊記憶體被釋放了,不被任何東西指向(不和任何東西有關係),於是再去析構stu3的時候,出問題了。。你已經和這塊地址沒有關係了,憑什麼讓你delete。而且也沒必要delete了,但是如果你知道這塊空間地址,並通過地址直接存取這塊空間還是能訪問到name的值的,因為值並沒有被刪除,修改代碼後,你會更加明白問題在哪裡,在5和6之間加上一句:cout<<"stu3的name指向地址:"<<(int *)stu3.name<<"\t"<<"stu4的name指向地址:"<<(int *)stu4.name<<endl;
輸出(您可以不要直接cout<<"stu3的name指向地址:"<<&stu3.name<<"\t"<<"stu4的name指向地址:"<<&stu4.name<<endl;這樣來訪問,這是擷取該指標在堆棧中的地址,而不是內容在堆中儲存的地址)
要解決這個問題。。看樣子只有重寫拷貝建構函式了(下面是重構拷貝建構函式)
student::student(const student& tempstu)
{
int len=strlen(tempstu.name);
name=new char[len+1];
strcpy(name,tempstu.name);
}
以為一切問題解決,可是啟動並執行時候
很明顯這是在析構1的時候,即析構stu1的時候,發現第7句stu5=stu1;這隻是一個賦值過程,因為stu5對象在第6句就構造了,賦值的過程的話,即依然是stu5.name=stu1.name,然後問題原因道理同上。這就是所謂的淺複製的過程,這也是淺複製容易導致的問題(大家可以去研究淺複製和深複製)
至於結構中的最後 的剩餘個數為什麼編程-2了,(其實這個例子模仿c++ primer),主要是因為位置4調用了fun(stu2);-這個調用,傳遞了一個實參為stu2的類對象,在傳遞過程類似
student funs=stu2;這個funs對象在fun方法返回時會銷毀,銷毀就會調用解構函式num--,而且在第5位置處,是通過student stu4=stu3這種拷貝建構函式來執行個體化stu4,在重寫的拷貝建構函式中,我們並沒有讓num++,而析構他的時候卻有num--了,這就是為什麼最後num不會迴歸至初始0,而是-2