Kaini
Kaini

Reputation: 319

How to allocate an object with a complex constructor?

I think I know C++ reasonably well and I am thinking about implementing something a bit bigger than a "toy" program. I know the difference between stack- and heap-memory and the RAII-idiom.

Lets assume I have a simple class point:

class point {
public:
    int x;
    int y;
    point(int x, int y) : x(x), y(y) {}
};

I would allocate points always on the stack, since the objects are small. Since on 64-bit machines sizeof(point) == sizeof(void*), if a am not wrong, I would go even further and pass points by value by default.

Now lets assume a more complex class battlefield, that I want to use in the class game:

class battlefield {
public:
    battlefield(int w, int h, int start_x, int start_y, istream &in) {
        // Complex generation of a battlefield from a file/network stream/whatever.
    }
};

Since I really like RAII and the automatic cleanup when an object leaves the scope I am tempted to allocate the battlefield on the stack.

game::game(const settings &s) :
        battlefield(s.read("w"), s.read("h"), gen_random_int(), gen_random_int(), gen_istream(s.read("level_number"))) {
    // ...
}

But I have several problems now:

I see two solutions to this problem:

I hope you can see the problem I am thinking of. Some questions that arise for me are:

Upvotes: 4

Views: 1088

Answers (2)

Sanish
Sanish

Reputation: 1719

But this approach has the problem that I have a half-initialized object, aka an object that violates the RAII-idiom.

That is not RAII. The concept is you use objects to manage the resources. When you aquire a resource like heap memory, semaphore, file handle, you have to transfer the ownership to a resource managing class. This is what smart pointers in C++ are meant for. You have to use either unique_ptr if you want to have sole ownership of the object or use a shared_ptr if you want multiple pointers to have ownership.

Or I allocate battlefield on the heap in the game constructor. But I have to beware of exceptions in the constructor and I have to take care that the destructor deletes the battlefield.

If your constructor throws an exception, then the destructor of the object would not be called and you might end up in a half-cooked object. In this case, you have to remember what allocations you did in the constructor before the exception was thrown and deallocate all those. Again smart pointers will help automatic cleaning of resources. See this faq

Which objects are allocated on the heap, which ones are allocated on the stack? Why?

Try to allocate the objects in stack whenever possible. Your objects then have life only in the scope of that block. If you have a case where this is not possible go for heap allocation - eg: you only know the size at runtime, the size of the object is too big to sit on stack.

Upvotes: 2

CB Bailey
CB Bailey

Reputation: 791769

You could let your battlefield be constructed from settings:

explicit battlefield(const settings& s);

or alternatively, why not create a factory function for your battlefield?

E.g.

battlefield CreateBattlefield(const settings& s)
{
    int w = s.read("w");
    int h = s.read("w");
    std::istream& in = s.genistream();
    return battlefield(w, h, gen_random_int(), gen_random_int(), in);
}

game::game(const settings &s) :
    battlefield(CreateBattlefield(s)) {
    // ...
}

Upvotes: 2

Related Questions