TheUnimake
TheUnimake

Reputation: 31

Why doenst STL vector call default constructor on allocation?

I got the follwing block of code:

#include <vector>
#include <iostream>

struct TestStruct {
    bool wasCreated;

    TestStruct() {
        std::cout << "Default Constructor" << std::endl;
        wasCreated = false;
    }

    ~TestStruct() {
        if (wasCreated) {
            DoImportantStuff();
        }
    }

    void Create() {
        wasCreated = true;
    }

    // delete all copy stuff
    TestStruct(const TestStruct&) = delete;
    TestStruct& operator=(const TestStruct&) = delete;


    // implement only move assignment & ctor
    TestStruct(TestStruct&& other) {
        std::swap(wasCreated, other.wasCreated);
    }

    TestStruct& operator=(TestStruct&& other) {
        std::swap(wasCreated, other.wasCreated);
        return *this;
    }

    // very important stuff
    void DoImportantStuff() {
        std::cout << "Do Important Stuff" << std::endl;
    }
};

int main() {
   
    std::vector<TestStruct> testVector;

    testVector.emplace_back(TestStruct());
    testVector.emplace_back(TestStruct());

    std::cin.get();
}

This code leads to the output:

Default Constructor

Do Important Stuff

Default Constructor

Do Important Stuff

Do Important Stuff

Basicly I wanted to write a class, which owns memory but allocates this memory only when I call Create(). To avoid memory leaks and to avoid deleting not allocated memory I introduced wasCreated which will be only true when i call Create(). Every TestStruct should be saved in one vector. So in implemented move assigment & ctor and deleted both copy assigment & ctor.

Now it seems to me that the vector doenst call interally the default constructor of my TestStruct when its allocates new memory. Why is so and how do get the vector to call the default constructor on memory allocation? Do I need my own allocator?

Upvotes: 1

Views: 820

Answers (2)

bogdan
bogdan

Reputation: 9317

Your problem is that your move constructor is implemented incorrectly. It swaps wasCreated between the newly created object and the one being moved from, but the variable in the newly created object has not been initialized yet (a default-constructed bool has an unknown value). So your temporary objects created with TestStruct() receive an uninitialized bool, which happens to be true in your case, hence the calls to DoImportantStuff() in their destructors.

So the move constructor should look something like this:

// implement only move assignment & ctor
TestStruct(TestStruct&& other) : wasCreated(other.wasCreated) {
    other.wasCreated = false;
}

(You have moved ownership to the newly created object, the old one doesn't own anything anymore.)

Don't confuse the assignment operator with the constructor; they do different things. The assignment operator deals with two objects that are both already constructed; in the case of the constructor, the object being constructed is, well..., not constructed yet, so it doesn't have a valid state.

By the way, emplace_back() is pointless the way you're using it. Its purpose is to forward its arguments directly to the constructor of the object inside the vector. Since you have a default constructor (no arguments), the call should be:

testVector.emplace_back();

This will default-construct the TestStruct in place.

Upvotes: 4

NPE
NPE

Reputation: 500673

Now it seems to me that the vector doenst call interally the default constructor of my TestStruct when its allocates new memory.

A default-constructed vector has zero size, so there are no objects to construct.

If you want the vector to default-construct some objects, resize it.

Upvotes: 0

Related Questions