vector如何析构它的对象
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
是一个类而不是标准数据类型时)。
当vector中的元素是对象时,这可能导致严重的资源泄漏。我对这个问题的解决方案是,在我free()
内存之前显式地调用~T()
。
我使用malloc()
的原因是,我试图利用realloc()
。如果我使用new
和delete
,内存使用将在重新分配时达到峰值。(新缓冲区和旧缓冲区同时存在的时刻)
问:这是一个糟糕的设计吗?std::vector
如何解决这个问题?我的向量类还有其他缺陷吗?
PS:现在先不谈malloc()
的多线程性能
调用~T()
正是 std::vector
处理问题的方式。
你确实有几个问题:
首先,push_back
需要使用位置new将值复制构造到vector中。你不能只用赋值。
其次,你不能调用realloc
——如果对象有内部指针,它们最终会指向它们自身之外。您必须再次调用malloc
,然后使用placement new来复制构造这些值,然后显式地删除所有旧值,然后调用free
来释放旧值。
(实际上,std::vector
不调用~T()
本身。而是调用分配器,分配器负责…分配和释放内存。但在内部,这就是通用分配器的工作方式。
push_back(const T & T)添加一个元素,必要时调用realloc()
只要T
是trivially copiable
就可以,例如,尝试推回双链表,在重新分配后取一个并向后迭代-应用程序可能会崩溃。解决方案是重载该函数两次,一次用于一般可复制的类型,另一次用于不可复制的对象。
与其他容器相反,我很抱歉,标准容器不使用realloc
来复制那些微不足道的对象。至少在Windows上,realloc
首先检查当前块是否可以容纳新的大小,如果可以,它只是更新堆条目,导致巨大的性能提升(不复制)。
在free()释放内存之前显式调用~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
将确保调用实例dr。div =)
在极限情况下,如果默认构造函数为刚分配的对象保留,并且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;
}
它打印:
5
4
3.
2
1
0
-1
2
3
4
5
---------------
你好世界
也许我写错了。如果你知道,请告诉我。
- 什么时候调用组成单元对象的析构函数
- 使用基类指针创建对象时,缺少派生类析构函数
- 对具有动态分配的内存和析构函数的类对象的引用
- C++析构函数调用两次,堆栈分配的复合对象
- 如何从 Gtk::窗口调用派生对象的析构函数
- 为什么数组中对象的析构函数在被另一个对象替换时不被调用?
- 为什么为未删除的对象调用析构函数?
- 对象的构造函数和析构函数
- 循环中本地对象的析构函数是否保证在下一次迭代之前被调用?
- C++使用函数对象的线程,如何调用多个析构函数而不是构造函数?
- 从未调用shared_ptr对象的析构函数
- 是否可以在其析构函数中使用指向已销毁对象的指针?
- 使用私有析构函数删除动态分配的对象
- 为什么在这里调用析构函数,以及在调用该对象析构函数后如何调用对象成员函数?
- 对象析构函数在多线程处理时不断被调用,但该对象并未超出范围
- C++包含包含指针的对象的对象析构函数
- C++类对象析构函数控件
- Objective-C++C++对象析构函数
- 结构对象析构函数
- 在Exit()时调用基本对象析构函数