0

Lets take custom vector implementation as an example:

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;
};

So my question is, how to make myVector be value-initialized in case I'll initialize it as:

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

Here, it contains 5 int values, so I need it to be all zeros; same with other types. I commented out _buff[i] = 0; as it's specific to int. Please give me some hints.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
user2376997
  • 501
  • 6
  • 22

2 Answers2

3

It's as simple as

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

Alternatively, you could get rid of the loop and add a pair of {} (or ()) here:

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

But this option would value-initialize all _capasity objects, rather than the first _size ones, as noted by @bipll.


Also, note that if you want to mimic the behavior of std::vector, you need to allocate raw storate (probably std::aligned_storage) and call constructors (via placement-new) and destructors manually.

If Object is a class type, _buff = new Object[_capasity]; calls default constructors for all _capasity objects, rather than for the first _size objects as std::vector does.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
1

Note that when calling

        _buff = new Object[_capasity];

(btw, why have you moved this initialization out of init-list, into constructor body?) you already have default-initialized _capasity objects. Default initialization has the following effects here: while elements of scalar type would remain uninitialized (and reading from them UB), for class types you have already called _capasity constructors.

To avoid unnecessary constructions you have the following possible options, among others:

  1. Use std::aligned_alloc to allocate non-initialized memory:

    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
    }
    

    Remember that again buff_ should be aligned_alloced when vector grows (can be std::realloc()ed for trivial types), and in destructor it should be std::free()d — and prior to that size_ objects inside it should be destructed (with an explicit call to ~Object()).

  2. Change buff_'s type to something more trivial yet properly aligned:

        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]{};
        }
    

    Again, in destructor, objects should be manually destroyed, but this time buff_ can be simply delete[]d afterwards.

bipll
  • 11,747
  • 1
  • 18
  • 32