vector如何析构它的对象

How does std::vector destruct its objects?

本文关键字:对象 析构 何析构 vector      更新时间:2023-10-16

为了练习,我正在尝试实现我自己的std::vector。当前源代码:http://pastebin.com/bE4kjzcb


这是我的课程大纲:

  1. Array()使用malloc()分配内存
  2. push_back(const T &t)增加一个元素,必要时调用realloc()
  3. ~Array()调用free()释放内存

这个模型的主要问题是,free()回收内存,但它不调用T的析构函数(当T是一个类而不是标准数据类型时)。

当vector中的元素是对象时,这可能导致严重的资源泄漏。我对这个问题的解决方案是,在我free()内存之前显式地调用~T()

我使用malloc()的原因是,我试图利用realloc()。如果我使用newdelete,内存使用将在重新分配时达到峰值。(新缓冲区和旧缓冲区同时存在的时刻)

问:这是一个糟糕的设计吗?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()

只要Ttrivially 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[]

与其调用mallocfree,不如使用newdelete。调用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

---------------

你好世界

也许我写错了。如果你知道,请告诉我。