Значение C ++ инициализирует элементы пользовательского контейнера - PullRequest
0 голосов
/ 29 декабря 2018

Давайте возьмем в качестве примера пользовательскую реализацию 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.Пожалуйста, дайте мне несколько советов.

Ответы [ 2 ]

0 голосов
/ 29 декабря 2018

Обратите внимание, что при вызове

        _buff = new Object[_capasity];

(кстати, почему вы переместили эту инициализацию из списка инициализации в тело конструктора?), У вас уже есть инициализированные по умолчанию объекты _capasity.Инициализация по умолчанию имеет здесь следующие эффекты: хотя элементы скалярного типа остаются неинициализированными (и читают из них UB), для типов классов вы уже вызвали _capasity конструкторы.

Чтобы избежать ненужных конструкций, у вас есть следующееВозможные варианты, среди прочего:

  1. Используйте std :: align_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 ed при росте вектора (может быть 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[] d впоследствии.

0 голосов
/ 29 декабря 2018

Это так же просто, как

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

Кроме того, вы можете избавиться от цикла и добавить пару {} (или ()) здесь:

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

Но эта опция будет инициализировать все _capasity объекты, а не первые _size объекты, как отмечено @bipll.


Также обратите внимание, чтоесли вы хотите имитировать поведение std::vector, вам нужно выделить необработанные хранилища (вероятно, std::aligned_storage) и вызывать конструкторы (с помощью размещения нового) и деструкторы вручную.

Если Object является классомtype, _buff = new Object[_capasity]; вызывает конструкторы по умолчанию для всех _capasity объектов, а не для первых _size объектов, как std::vector.

...