C++值初始化自定义容器的项

C++ value initialize items of a custom container

本文关键字:自定义 初始化 C++      更新时间:2023-10-16

让我们以自定义vector实现为例:

template<typename Object>
class myVector {
public:
explicit myVector(int size = 0) :
_size{ size },
_capasity{ size + SPARE_CAPACITY }
{
_buff = new Object[_capasity];
if (_size > 0) {
for (int i = 0; i < _size; i++) {
//_buff[i] = 0;
}
}
}
// more code
private:
Object * _buff = nullptr;
int _size;
int _capasity;
};

所以我的问题是,myVector如果我将其初始化为:

int main() {
myVector<int> v02(5);                   
}

在这里,它包含 5 个int值,所以我需要它全为零;与其他类型相同。我注释掉了_buff[i] = 0;,因为它特定于int.请给我一些提示。

它就像

for (int i = 0; i < _size; i++)
_buff[i] = Object{};

或者,您可以摆脱循环并在此处添加一对{}(或()):

_buff = new Object[_capasity]{};
//                           ^^

但是,正如@bipll所指出的,此选项将值初始化所有_capasity对象,而不是前_size对象。


另外,请注意,如果你想模仿std::vector的行为,你需要分配原始存储(可能是std::aligned_storage)并手动调用构造函数(通过放置新)和析构函数。

如果Object是类类型,则_buff = new Object[_capasity];为所有_capasity对象调用默认构造函数,而不是像std::vector那样为前_size对象调用默认构造函数。

请注意,当调用时

_buff = new Object[_capasity];

(顺便说一句,你为什么要把这个初始化从初始化列表中移到构造函数体中?)你已经有了默认初始化的_capasity对象。默认初始化在这里具有以下效果:虽然标量类型的元素将保持未初始化状态(并从中读取 UB),但对于已经调用_capasity构造函数的类类型。

为避免不必要的构造,您有以下可能的选项:

  1. 使用 std::aligned_alloc 分配未初始化的内存:

    explicit myVector(std::size_t size = 0) :
    size_{ size }
    , capacity_{ size + SPARE_CAPACITY }
    , buff_{std::aligned_alloc(alignof(Object), _capacity)}
    {
    if(!buff_) throw std::bad_alloc();
    if(size) new (buff_) Object[size]{}; // empty braces answer your original query
    }
    

    请记住,当向量增长时,buff_应该再次aligned_alloc(对于平凡类型可以std::realloc()ed),并且在析构函数中它应该std::free()d — 在此之前size_它里面的对象应该被破坏(显式调用~Object())。

  2. buff_的类型更改为更简单但正确对齐的类型:

    using Storage = std::aligned_storage_t<sizeof(Object), alignof(Object)>;
    Storage *buff_;
    Object *data_ = nullptr;
    public:
    explicit myVector(std::size_t size = 0) :
    size_{ size }
    , capacity_{ size + SPARE_CAPACITY }
    , buff_{new Storage(_capacity)}
    {
    if(size) data_ = new (buff_) Object[size]{};
    }
    

    同样,在析构函数中,对象应该手动销毁,但这次buff_可以简单地delete[]之后。