Jump to content
  • Advertisement
Sign in to follow this  
Vincent_M

Constructor Initializer List and Heap Allocation

This topic is 1084 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Whenever I have a field that should be initialized onto the heap upon initialization in a class, I usually initialize it to nullptr in the initializer list, and then allocate it in the constructor's body. For example:

class Test
{
private:
Model *model;

public:

Test() : model(nullptr)
{
model = new Model();
}

~Test()
{
if(model) delete model;
}

};

I notice in Qt, that my designer-derived class will allocate its UI pointer onto the heap in the constructor's initializer list. Is this a good practice? If my heap-allocated objects' constructors are lean, would this make better sense?

Share this post


Link to post
Share on other sites
Advertisement

It can be a good practice, and it mostly depends on the object lifetimes and behavior.

 

In QT's case, the pointer is back to the parent, containing object. The UI object should always have a shorter life than its parents, and it needs a pointer to send messages back up stream. 

 

Think about those carefully. The object lifetimes of the owner will always exceed the lifetime of the child, and the child needs a way to communicate back to the parent. When both are true, the design works well.

 

If there were some way the parent or owner could have a shorter lifetime than the child then the design would be problematic. In that case the UI object would be holding a dead pointer which is a very bad thing; instead it should go through a handle or similar protected indirection that would consume messages to the now-missing endpoint. But since that isn't the design and object lifetimes work out, keeping a pointer to the parent works well.

 

Edit: Ah- misunderstood your concern. Looks like Josh Petrie handled that detail.  

Edited by frob

Share this post


Link to post
Share on other sites

For the purpose of error-checking, a better alternative is, as you mention, initialize mem-pointers to nullptr in the constructor. Then implement another class function such as bool Init(). In that Init() call, you can perform required allocations and other initializations as desired. The benefit of a separate Init() call: you can return an error indication if any part of your initialization fails.

Share this post


Link to post
Share on other sites

Your Test class is broken, because you did not follow the rule of 0/3/5. It will not work if its copied somewhere.

You are better off not using a raw pointer and not using new/delete. Just declare it as a normal member variable or if thats not possible use std::unique_ptr, which handles all difficult things automatically.

Share this post


Link to post
Share on other sites

If you're using C++11, you can just initialize to nullptr in the class body's definition.
 

class Test
{
private:
    Model *model = nullptr; //Initialized.

public:
    Test() //: model(nullptr) <-- Not needed
    {
        model = new Model();
    }
    ~Test()
    {
        delete model;
    }
}

Also, you may want to also consider smart pointers. A std::unique_ptr is usually the same size as a regular pointer*, and deletes itself when the class gets deleted. Depending on your performance needs, a unique_ptr for situations like this may save some programmer mistakes.

 

Alternatively, 'model' might not need to be heap-allocated at all depending on what you are doing.

 

*if using the default deleter.

 

=================

 

As far as the initialization list goes, there's a gotcha you need to be careful about:

class Something;

class Test
{
public:
    Test() : something(this) //Careful when passing the 'this' pointer in the initializer list.
    {
        something.DoSomething(); //Fine. 'something' is fully constructed.
    }
    
    void Blah()
    {
        //...
    }
    
private:
    Something something;
};

class Something
{
public:
    Something(Test *test) : test(test) //Fine, we're just storing the pointer address.
    {
        test->Blah(); //ERROR! 'Test' hasn't been fully constructed yet!
    }
    
    void DoSomething()
    {
        test->Blah(); //Fine. 'test' is fully constructed.
    }
    
private:
    Test *test = nullptr;
};

If we initialize a member in an initialization list, and pass it a pointer to ourself, we haven't yet been fully constructed yet!

During the initialization list, our memory has already been allocated and the 'this' pointer points to the right space, but that memory hasn't been initialized yet, so that pointer can't be used to read or write to our members or call functions that read or write to the members. It'd be undefined behavior. It likely won't crash, because the pointer address points to legit memory, but the memory contents are still gibberish.

 

As far as using the 'this' pointer goes, we can store it, but we can't predictably use it until we reach the constructor's function-body (which is after the initializer list has already been gone over).

Edited by Servant of the Lord

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!