实现C ++模板列表类的高效初始化

时间:2017-04-20 00:28:16

标签: c++ templates

对于编写模板列表类,相当于矢量(纯粹作为设计练习),我试图找出有效的方法。

如果有人写道:

v = new T[size];

然后编译器会调用T的构造函数,对吧? T()

所以在下面的课程中:

v = (T*) new char[sizeof(T) * size]

这似乎很容易。但是,在析构函数中,如何删除已初始化的那些?在下面的类中,只初始化了第一个“used”元素。

另外,在复制构造函数中,如何有效地仅为使用过的元素调用T的复制构造函数?

如果我初步确定了昂贵的方式,那就有效:

v = new T[size];
for (int i = 0; i < used; i++)
  v[i] = orig.v[i];

但这需要v已经使用T()进行了宣传。有什么更好的方法?

课程如下:

#include <cstdint>

template<typename T>
class List {
private:
    uint32_t used;
    uint32_t capacity;
    T* v;
public:
    List(uint32_t cap) : used(0), capacity(cap), v((T*)new char[sizeof(T)*capacity]) {}
    ~List() {
        delete [] v; // no
    }
    List(const List& orig) : used(orig.used), capacity(orig.capacity), v((T*) new char[sizeof(T)*capacity]) {
        // now copy only the used ones
        for (int i = 0; i < used; i++)
            v[i] = orig.v[i]; // no, operator = will call destructor on v[i], but it is uninitialized
    }
};

3 个答案:

答案 0 :(得分:1)

std::vector类似,您需要使用“placement new”并明确调用析构函数。

#include <new>

~List() {
    while (used) {
        --used;
        v[used]->~T();
    }
    delete[] reinterpret_cast<char*>(v);
}

List(const List& orig) : used(orig.used), capacity(orig.capacity),
    v(reinterpret_cast<T*>(new char[sizeof(T)*capacity])) {
    // now copy only the used ones
    for (int i = 0; i < used; i++)
        new(v+i) T(orig.v[i]);
}

请注意,上面的复制构造函数不是异常安全的。尽量做到这一点。

答案 1 :(得分:0)

首先,只需使用std::vector<T>而不是自己重新实现。

您在这里寻找的是贴装新显式析构函数调用。通常情况下,每个new应与delete配对,每个展示位置都应与显式析构函数调用配对。

回答您的具体问题:

  

但是,在析构函数中,如何只删除已经初始化的那些?

明确地调用他们的析构函数,然后正确delete[]原始char[]分配,这将(正确)自动调用任何T析构函数。

for (uint32_t i = 0; i < used; ++i) {
    v[i]->~T();
}

delete [] reinterpret_cast<char *>(v);
  

另外,在复制构造函数中,如何有效地仅为使用过的元素调用T的复制构造函数?

你需要在这里放置新的。您的行v[i] = orig.v[i];会导致未定义的行为,因为尚未构建v[i]

安置新对象(在使用之前应该对每个v[i]执行此操作):

new(reinterpret_cast<char *>(v + i)) T(orig.v[i]);

答案 2 :(得分:0)

对于复制构造函数,您可以尝试以下代码:

#include <cstring>

List(const List& orig) : used(orig.used), capacity(orig.capacity), v((T*) new char[sizeof(T) * capacity]) {
        // now copy only the used ones
        memcpy(v, orig.v, sizeof(T)*capacity);
    }

List(const List& orig) : used(orig.used), capacity(orig.capacity), v((T*) new char[sizeof(T) * capacity]) {
        // now copy only the used ones
        memcpy_s(v, capacity, orig.v, sizeof(T)*capacity);
    }