寫 python 寫久了,老實說長期以來都不是太在意記憶體管理的問題,畢竟在 python 裡主要就是靠 garbage collection ,不太需要自己動手。但跟著寫了 2 年多的 uTensor ,老實說跟其他開發者比起來我這方面確實是爛到見底,最近剛好沒工作空窗,就慢慢自己看一些 C++ 的東西,順便趁機問身邊 C++ 熟的朋友 XD。寫這篇記錄一下我問的一個奇怪的問題。
Copy Behavior
C++ 充滿著各種機制,雖說語法多元,但也造成初學者會有種語意不明的狀況。或許你可以說 spec 裡都有,但如果這是個理由的話,你應該會同意 JavaScript 是世界上最好的語言 (偷婊一下不犯法吧 XD)。
最近碰到的就是什麼時候物件會 Copy,尤其時考慮到 function return value 的時候。以下是我用來問我朋友用的範例:
#include <iostream>using namespace std;class A {
public:
int x, y;
A(): x(0), y(0) {}
A(int x, int y): x(x), y(y) {}
A(const A& other){
cout << "Copied" << endl;
this->x = other.x;
this->y = other.y;
}
}; this->x = other.x;
this->y = other.y;
}
};A& newA(int x, int y){
A* a = new A(x, y);
return *a;
}int main(int argv, char* argv[]){
A a;
a = newA(3, 3); A b = a;
return 0;
}
我的問題其實也很簡單: newA
會在 heap 上 allocate 一個 A
物件,然後我把那個物件 return 回去了,那是不是代表這個物件的生命週期就會被外面那個變數所管理?
一個奇怪的點是我的 copy constructor 只被 call 了一次,那問題的關鍵在於我把 copy assignment operator 跟 copy constructor 搞混了,更別提我還天真以為 C++ compiler 會在需要 copy assignment operator 的時候自動 去呼叫 copy constructor 。
先講結論: 不會,理由可以看這邊。
重點在於
- copy constructor,顧名思義是一個 constructor ,所以它只有在建構新物件時會被呼叫,所以說只有要透過一個現存的物件要去建構一個新物件時才會被呼叫
- copy assignment operator,是一個 assignment ,所以是當一個已經存在的物件要被賦予另一個已經存在的物件做為新值的時候才會被呼叫。
所以說當我寫下 a = new A(3, 3);
時,大概發生這些事:
- 在 heap 上被 allocate 一個
A
物件,並且它的 value 被回傳。 - 因為
a
是一個已經存在的物件,所以a = new A(3, 3);
呼叫了 copy assignment operator,把在 heap 上的 value copy 給了a
這解釋了為什麼 copy constructor 只被 call 一次,而且這也造成了 dangling 的 memory (因為 newA
裡的 new
沒有被 free 掉)。
一個有趣的修法是我們只要把 a
宣告成 A&
即可,這時候 a
會 reference 到 new
所產生的那個記憶體位址。