[英]How does std::vector destruct its objects?
為了練習,我正在嘗試實現自己的std :: vector。 當前源代碼: http : //pastebin.com/bE4kjzcb
這是我班級的概要:
Array()
使用malloc()
分配一些內存 push_back(const T &t)
添加一個元素,必要時調用realloc()
。 ~Array()
調用free()
來釋放內存。 這個模型的主要問題是free()
循環內存,但它不調用T
的析構函數(當T
是類而不是標准數據類型時)。
當向量內的元素是對象時,這可能導致嚴重的資源泄漏。 我解決這個問題的方法是,在free()
內存之前調用~T()
EXPLICITLY 。
我使用malloc()
的原因是,我正在嘗試使用realloc()
。 如果我使用new
和delete
,則重新分配時內存使用率將達到峰值。 (新緩沖區和舊緩沖區都存在的時刻。)
問:這是一個糟糕的設計嗎? std::vector
如何解決這個問題? 我的矢量類中還有其他缺陷嗎?
PS:現在不要談論malloc()
多線程性能。
調用~T()
正是 std::vector
處理問題的方式。
但是你有幾個問題:
首先, push_back
需要使用placement new來將值復制 - 構造到向量中。 你不能只使用作業。
其次,你不能調用realloc
- 如果對象有內部指針,它們最終會指向自己的外部。 您必須再次調用malloc
,然后使用placement new復制構造值,然后顯式刪除所有舊值,然后調用free
釋放舊值。
(實際上, std::vector
不會調用~T()
本身。而是調用分配器來負責... 分配和釋放內存。但在內部,這就是通用分配器的用法。)
push_back(const T&t)添加一個元素,必要時調用realloc()。
只要T
可以trivially copiable
,就可以了,例如,嘗試推回雙鏈表,並且在重新完成后需要一個並向后迭代 - 應用程序可能會崩潰。 解決方案是將函數重載兩次,一個用於可瑣事可復制的類型,另一個用於非瑣事的對象。
與其他人相反,我很遺憾標准容器不會將realloc
用於可以輕易處理的對象。 至少在Windows上, realloc
首先檢查當前塊是否可以保持新的大小,如果是 - 它只是更新堆條目,從而導致巨大的性能提升(無需復制)。
在我釋放()內存之前調用~T()顯式。
是的,這就是標准分配器的工作方式。 假設size
是對象計數,您迭代每個並手動銷毀它:
for (auto i=0U;i<size;i++){
data[i].~T();
}
有趣的是,C ++ 17將添加std::destruct
,它就是這樣做的。
獎勵:使用new[]
和delete[]
在這里沒有幫助。 通常,動態數組比實現容量所需的空間節省更多空間 ,額外的空間不是用活動對象填充,只是垃圾。
new[]
將用對象填充整個內存。 容量無法以這種方式實現。 每當有人推回新元素時,數組將移動/復制整個對象。 所以在1000次push_back
之后,將有1000次重新分配。 我們希望攤銷時間為O(log (n))
。
甚至標准的分配器也會調用new(size_t)
或malloc
而不是new[]
而不是調用malloc
和free
,更喜歡使用new
和delete
。 調用delete
將確保調用實例dtor。 =)
如果默認構造函數僅為已分配的對象保留,並且T的/ move / copy構造函數/賦值運算符和析構函數傳播剛分配的信息或用戶對象 ,則可以使用new [] 。 std :: vector中的解決方案及其默認分配器是一個更好的設計。
那個工程
buffer = new T[capacity];
代替
buffer = (T*)malloc(capacity * sizeof(T));
和
delete [] buffer;
代替
free(buffer);
將自動調用每個對象的析構函數,如示例所示
class A {
public:
~A() { std::cout << "ok" << std::endl; }
};
int main() {
A* a = new A[3];
delete [] a;
return 0;
}
此代碼輸出3“ok”。 然后,A應包含其他字段和非默認構造函數,以區分分配和用戶構造。
這里有一個例子,它是如何工作的或多或少std :: vector:
#ifndef __STDVECTOR__
#define __STDVECTOR__
#include <iostream>
using namespace std;
template <typename T>
class StdVector{
private:
T *buffer;
unsigned int capacity;
public:
//Constructor.
StdVector(){
capacity=0;
buffer=new T[capacity];
}
//Copy constructor.
StdVector(const StdVector &asv){
int i;
capacity=asv.getCapacity();
buffer=new T[asv.getCapacity()];
for (i=0; i<capacity; i++){
buffer[i]=asv[i];
}
}
//Destructor.
~StdVector(){
delete []buffer;
}
void push_back(T obj){
StdVector oldSV(*this);
int i;
capacity++;
delete []buffer;
buffer=new T[capacity];
for (i=0; i<oldSV.getCapacity(); i++){
buffer[i]=oldSV[i];
}
buffer[i]=obj;
};
T getBuffer() const{
if (capacity==0){
throw exception();
}
return *buffer;
};
T &operator[](int index) const{
if (index>=capacity){
//Out of range.
throw exception();
}
else{
return buffer[index];
}
}
StdVector &operator=(const StdVector &obj){
capacity=obj.getCapacity();
delete []buffer;
buffer=new T[capacity];
buffer=obj.getBuffer();
return *this;
}
unsigned int getCapacity() const{
return capacity;
};
};
#endif
int main(){
try{
StdVector<int> test;
StdVector<string> test2;
unsigned int i;
test.push_back(5);
test.push_back(4);
test.push_back(3);
test.push_back(2);
test.push_back(1);
test.push_back(0);
test.push_back(-1);
test.push_back(-2);
test.push_back(-3);
test.push_back(-4);
test.push_back(-5);
for (i=0; i<test.getCapacity(); i++){
cout << test[i] << endl;
}
test2.push_back("Hello");
test2.push_back(" ");
test2.push_back("World");
test2.push_back(".");
cout << "---------------" << endl;
for (i=0; i<test2.getCapacity(); i++){
cout << test2[i];
}
cout << endl;
}
catch(...){
cout << "Exception." << endl;
}
return 0;
}
它打印:
五
4
3
2
1
0
-1
-2
-3
-4
-5
---------------
你好,世界。
也許我有一些錯誤。 如果你知道的話,請跟我說。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.