std :: vector如何破坏它的对象?

时间:2016-08-10 15:47:47

标签: c++ memory-management vector

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

以下是我班级的概要:

  1. Array()使用malloc()
  2. 分配一些内存
  3. push_back(const T &t)添加一个元素,必要时调用realloc()
  4. ~Array()致电free()以释放记忆。
  5. 这个模型的主要问题是,free()回收内存,但它不会调用T的析构函数(当T是类而不是标准数据类型时) 。

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

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

    问: 这是一个糟糕的设计吗? std::vector如何解决这个问题?我的矢量类中还有其他缺陷吗?

    PS:我们现在不讨论malloc()的多线程表现。

5 个答案:

答案 0 :(得分:5)

调用~T() 完全 std::vector处理问题的方式。

但是你确实遇到了一些问题:

首先,push_back需要使用placement new来将值复制 - 构造到向量中。你不能只使用作业。

其次,你不能调用realloc - 如果对象有内部指针,它们最终会指向自己的外部。您必须再次调用malloc,然后使用placement new复制构造值,然后明确删除所有旧值,然后调用free以释放旧值。

(实际上,std::vector并不会调用~T()本身。而是调用负责... 分配和释放内存的分配器。 ,这就是通用分配器如何做到的。)

答案 1 :(得分:2)

  

push_back(const T& t)添加一个元素,必要时调用realloc()。

只要trivially copiablerealloc就可以了,例如,尝试推回双链表,并且在重新完成后再取一个并向后迭代 - 应用程序可能会崩溃。解决方案是将函数重载两次,一个用于可琐事可复制的类型,另一个用于非琐事的对象。

与其他人相反,我很遗憾标准容器不会将realloc用于可以轻易处理的对象。至少在Windows上,size首先检查当前块是否可以保持新大小,如果是 - 它只是更新堆条目,从而导致巨大的性能提升(无需复制)。

  

在释放()内存之前显式调用~T()。

是的,这就是标准分配器的工作方式。假设for (auto i=0U;i<size;i++){ data[i].~T(); } 是对象计数,您迭代每一个并手动销毁它:

std::destruct

有趣的是,C ++ 17将添加new[],这正是如此。

<强> 加成: 使用delete[]new[]在这里无济于事。通常,动态数组比实现容量所需的空间节省更多空间,额外的空间不是用活动对象填充,只是垃圾。

push_back将用对象填充整个内存。容量无法以这种方式实现。每当有人推回新元素时,数组将移动/复制整个对象。因此,在1000 O(log (n))之后,将有1000次重新分配。我们希望摊销时间为new(size_t)

即使标准分配器也会调用mallocnew[]而不是{{1}}

答案 2 :(得分:0)

而不是致电mallocfree,而是使用newdelete。调用delete将确保调用实例dtor。 =)

答案 3 :(得分:0)

如果默认构造函数是为刚刚分配的对象保留的,并且/ move / copy构造函数/赋值运算符和,那么可以使用 new [] T的析构函数传播刚刚分配的用户对象。 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&#34; ok&#34;。然后A应包含其他字段和非默认构造函数,以区分分配和用户构造。

答案 4 :(得分:-1)

这里有一个例子,它是如何工作的或多或少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

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

你好,世界。

也许我有一些错误。如果你知道的话,请跟我说。