placement new to defer to a different constructor

霸气de小男生 提交于 2019-11-30 18:09:17

By the time you enter the open curly brace of the Foo(int) constructor, all class members have had their constructor called. If you then force a call to another constructor with placement new, you're overwriting the current state of the class. This basically means all members have their constructors called twice - if something does new in its constructor, you leak that content, and you will really, really mess things up! You're effectively constructing two objects, and the destructors for the members of the first object are never called, since the second object overwrites the memory of the first object.

In other words it's BAD! Don't do it!!

The most common workaround is to use some kind of initialisation function, and call that from both constructors. This won't let you initialize const members and others that must be in the initializer list, though.

One worry I have is if Foo uses multiple inheritance you'll need to cast the this pointer to the most base class first. Othewise if the the this is offset (sometimes happens in multiple inheritance) it'll construct at the wrong address.

You wouldn't be safe if you extended another class and that class had a destructor, for example

class Foo
{
    int* a;
public:
    Foo():a(new int)
    {

    }
    ~Foo(){delete a;}
}

class Bar:public Foo
{
    Bar()
    {
        // initialize things
    }

    Bar( int )
    {
         new ( this ) Foo();
    }
}

First Bar(int) calls Foo(), then it calls Bar() which also calls Foo(). The second time Foo() is called, it overwrites the pointer set up by the first call to Foo(), and the allocated memory is leaked.

The key problem here is that constructors are special - when you write a construct that calls a constructor (for example use new keyword to create an object) not only the constructor body is executed, instead the whole chain of objects is constructed first.

So when you use placement-new syntax to run another constructor first C++ automagically reruns all the base class object constructors and all the member variables constructors and only then the other constructor body is invoked. Sometimes you'll be okay, but many times you will run into unexpected behavior.

It looks like the best solution to this problem is to just create a different function to do the initialization:

class Foo
{
    inline void nullify()
    {
        // initialize things
    }

    Foo()
    {
        nullify();
    }

    Foo( int )
    {
        nullify();
    }
}

As others said, is a bad idea, and as a possible destructive case: what if you do

class Foo
{
    Foo()
    {
        // initialize things
    }

    Foo( int bar )
    {
         new ( this ) Foo(bar);
    }
}

welcome no the land of infinite recursion.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!