所有容器提供的都是「value語意」而非「reference語意」。容器內進行元素的安插操作時,內部實施的是拷貝操作,置於容器內。因此stl容器 的每乙個元素都必須能夠拷貝。---《以vector為例,往vector中(實際上所有stl容器都是這樣)放元素,vector會呼叫元素類的拷貝建構函式生成的副本,當 vector走出生存期時(),會自動呼叫其中每個元素的析構函式。比如,如果 vectora,然後a.push_back(b);push_back其實是呼叫myclass的拷貝建構函式將引數b拷貝進去的。由於vector是自動管理的, 出了a的作用域外,a會自動消失釋放記憶體。之前push_back到a裡面的資料b通過呼叫b.~myclass()釋放記憶體。
[cpp]view plain
copy
#include
using
namespace std;
class cdemo
~cdemo()
char *str;
};
int main()
1. vector *a1 = new vector (); a1是new出來的,所以必須要手工delete.這是對a1本身而言,而與a1內儲存的資料無關。
2. a1 -> push_back(d1); 這部操作比較複雜,因為你的vector是儲存類,而不是類指標。但是push_back的引數是引用,所以不需要建立d1的拷貝(也就不需要呼叫拷貝構 造)作為引數傳遞給push_back。但是在push_back中,會建立d1的拷貝d1_1(需要呼叫拷貝構造),d1_1是儲存在a1管理的記憶體 中。
3. delete a1; a1中存有d1_1,所以會刪除d1_1,自然會呼叫d1_1的析構函式。
4. 在main中return 0, d1被自動刪除,此時呼叫d1的析構函式。
5. 因為class cdemo沒有拷貝建構函式,所以建立拷貝時只是簡單的把新物件中每個成員變數的值設定成與原來的物件相等。相當於執行memcpy。這時問題就來了,因 為你的乙個成員是char *str; 這樣d1,d1_1的str都是指向同乙個位址。所以只有第一次呼叫cdemo的析構函式時能執行正確,以後的都會出錯。因為乙個位址只能釋放一次。
解決辦法是定義自己的拷貝建構函式實現深拷貝:
[cpp]view plain
copy
#include
#include
#include
using
namespace std;
ofstream out("test.out");
class cdemo
cdemo(const cdemo &cd)
~cdemo()
} char *str;
};
int main()
1constructor is called
/copy constructor is called
/constructor is called
/copy constructor is called
copy constructor is called //搬運之前的元素
destructor is called //之前的元素被析構
/destructor is called //析構容器中所有物件
destructor is called
destructor is called //析構cdemo d1
destructor is called //析構cdemo d2
可以看到,再加入第二個物件的時候,拷貝建構函式被呼叫的兩次,這是因為,在第一次push_back時,vector的capacity是1,是 正常呼叫一次拷貝構造;而第二次push_back時,會發現容量不夠了,stl會重新分配乙個old_size的兩倍的空間,除了新push進來的資料 會放在這個新空間,呼叫一次拷貝構造,原來已經有的資料也要搬過來的,就又要呼叫拷貝構造。如果把a1 -> reserve(1);修改為a1 -> reserve(2);保證第二次時也有足夠的空間,那麼程式的執行結果為:
2constructor is called
/copy constructor is called
/constructor is called
/copy constructor is called
/destructor is called
destructor is called
destructor is called
destructor is called
為了進一步驗證空間重新分配對物件拷貝的影響,看下面的例子:
[cpp]view plain
copy
#include
#include
#include
using
namespace std;
ofstream out("test.out");
class cdemo
cdemo(const cdemo &cd)
~cdemo()
} char *str;
};
int main()
out << "/"
<< endl;
delete a1;
return 0;
}
程式的執行結果:/1
constructor is called
begin to push_back
the vector capacity is 1
copy constructor is called
destructor is called/2
constructor is called
begin to push_back
the vector capacity is 1
copy constructor is called
copy constructor is called //搬運之前的元素1
destructor is called //之前的元素被析構
destructor is called //cdemo d臨時物件/3
constructor is called
begin to push_back
the vector capacity is 2
copy constructor is called
copy constructor is called //搬運之前的元素1
copy constructor is called //搬運之前的元素2
destructor is called //之前的元素1被析構
destructor is called //之前的元素2被析構
destructor is called //cdemo d臨時物件/4
constructor is called
begin to push_back
the vector capacity is 4
copy constructor is called //不需要搬運,第三次時容量已經變成4
destructor is called //cdemo d臨時物件
/destructor is called //析構容器中所有物件
destructor is called
destructor is called
destructor is called
可以看到,容量的確是按照兩倍的空間遞增,並且原來已經有的資料要搬過來,就要呼叫拷貝構造。所以為了程式的效率,最好一開始就用reserve確定vector的大小,避免之後動態擴充套件,搬運原有資料引起拷貝構造的呼叫。
STL容器的拷貝構造和賦值特性
拷貝構造 stl容器都支援元素的插入操作,但是當你插入自定義類物件你必須要清楚的了解stl容器是如何完成插入,對插入的自定義類物件有什麼基本的要求。否則會出現你想不到的錯誤。舉例說明。class testcpyconstruct testcpyconstruct private enum char ...
拷貝建構函式與賦值建構函式
include stdafx.h include include using namespace std class a a a a 過載拷貝函式 a int id,char t name a char name a operator a a 注意 此處一定要返回物件的引用,否則返回後其值立即消失!...
拷貝建構函式與賦值建構函式
什麼時候用拷貝建構函式,和賦值建構函式 一 當用乙個已初始化過了的自定義類型別物件去初始化另乙個新構造的物件的時候,拷貝建構函式就會被自動呼叫。也就是說,當類的物件需要拷貝時,拷貝建構函式將會被呼叫。以下情況都會呼叫拷貝建構函式 乙個物件以值傳遞的方式傳入函式體 乙個物件以值傳遞的方式從函式返回 乙...