前言 最近在學習C++的類如何構造,在W3Cschool上看到關於拷貝構造函數的一個例子,記錄一下。 案例背景 這篇文章大致是構造瞭如下的一個Line類: class Line{ public: int getLength(void); Line(int len); // 簡單構造函數 Line(c ...
前言
最近在學習C++的類如何構造,在W3Cschool上看到關於拷貝構造函數的一個例子,記錄一下。
案例背景
這篇文章大致是構造瞭如下的一個Line
類:
class Line{
public:
int getLength(void);
Line(int len); // 簡單構造函數
Line(const Line &obj); // 拷貝構造函數
~Line(); // 析構函數
private:
int *ptr; //指向length
};
其中構造函數和析構函數的定義如下:
- 簡單構造函數:
Line::Line(int len){
cout<< "Normal constructor allocating ptr." <<endl;
// 為指針分配記憶體
ptr = new int;
*ptr = len;
}
- 拷貝構造函數:
Line::Line(const Line &obj){
cout<< "Copy constructor allocating ptr." <<endl;
ptr = new int;
// copy the value
//這裡右式的運算順序是先獲取obj.ptr,再用'*'取值.
//因為是複製值,而不是複製地址,所以'='兩邊都要加上'*',
//否則,多個Line對象的長度都會被綁定到一起。
*ptr = *obj.ptr;
}
- 析構函數(在對象被銷毀時執行):
Line::~Line(void){
cout<< "Freeing memory!"<<endl;
delete ptr;
}
- 獲取
Line
對象的長度,直接返回指針指向的int
類型數據
int Line::getLength(void){
return *ptr;
}
- 定義一個
display
函數,用於輸出Line
對象的長度:
void display(Line obj){
cout<< "Length of line : "<<obj.getLength() <<endl;
}
正文
對於以下main
函數的內容:
int main(){
Line line1(10);
Line line2(line1); //這裡調用了拷貝構造函數
display(line1);
display(line2);
return 0;
}
預期的輸出是:
Normal constructor allocating ptr.
Copy constructor allocating ptr.
Length of line : 10
Length of line : 10
Freeing memory!
Freeing memory!
但實際輸出是:
拷貝構造函數和析構函數被調用了好幾次
Normal constructor allocating ptr.
Copy constructor allocating ptr.
Copy constructor allocating ptr.
Length of line : 10
Freeing memory!
Copy constructor allocating ptr.
Length of line : 10
Freeing memory!
Freeing memory!
Freeing memory!
分析
在設置斷點和調試代碼之後,發現原因:
- display函數的函數參數是值傳遞,也就是說在調用時會創建函數參數(Line對象)的副本,並且display函數執行完之後,副本會被刪除。
- 也就是說,每執行一次display函數,都會觸發對拷貝構造函數和析構函數的調用,就會輸出如下的文本:
Copy constructor allocating ptr.
Length of line : 10
Freeing memory!
- 而輸出結尾的兩個
Freeing memory!
是由於C/C++
的局部變數是存儲在棧區stack
的。棧區由編譯器自動分配和釋放記憶體。 - 當程式執行到
return 0;
的時候,局部變數line1
和line2
被銷毀,故析構函數被調用。 - 並且需要註意的是,這兩個輸出的順序是:
Freeing memory! --> 對應line2的銷毀
Freeing memory! --> 對應line1的銷毀
- 這是因為變數是存儲在棧區中的,遵循FILO(First In, Last Out)的順序。