Размещение нового для переноса на другой конструктор

Это безопасно? Я не использую никаких виртуальных функций в своей реальной реализации, но я склонен полагать, что даже если бы я был, это все равно было бы безопасно.

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

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

Ответ 1

К моменту ввода открытой фигурной скобки конструктора Foo(int) все члены класса имеют свой конструктор. Если затем принудительно вызвать другой конструктор с новым местом размещения, вы переписываете текущее состояние класса. Это в основном означает, что все члены имеют свои конструкторы, которые называются дважды - если что-то делает new в своем конструкторе, вы пропускаете этот контент, и вы действительно действительно испортите вещи! Вы эффективно создаете два объекта, а деструкторы для членов первого объекта никогда не вызываются, поскольку второй объект перезаписывает память первого объекта.

Другими словами это БАД! Не делай этого!

Наиболее распространенным обходным решением является использование какой-либо функции инициализации и вызов этого из обоих конструкторов. Это не позволит вам инициализировать члены константы и другие, которые должны быть в списке инициализаторов.

Ответ 3

Одно беспокойство, которое у меня есть, - если Foo использует множественное наследование, вам нужно будет приложить указатель this к самому базовому классу. В противном случае, если this смещается (иногда это происходит при множественном наследовании), он будет строиться с неправильным адресом.

Ответ 4

Вы не были бы в безопасности, если бы вы расширили другой класс, и этот класс имел деструктор, например

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

    }
    ~Foo(){delete a;}
}

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

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

Сначала Bar(int) вызывает Foo(), затем он вызывает Bar(), который также вызывает Foo(). Во второй раз Foo() вызывается, он перезаписывает указатель, настроенный первым вызовом на Foo(), и выделенная память просочилась.

Ответ 5

Основная проблема заключается в том, что конструкторы являются особыми - когда вы пишете конструкцию, которая вызывает конструктор (например, используйте ключевое слово new для создания объекта), выполняется не только тело конструктора, а целая цепочка объектов построенный первым.

Поэтому, когда вы используете синтаксис размещения-нового для запуска другого конструктора, сначала С++ автоматически запускает все конструкторы объектов базового класса и все конструкторы переменных-членов, и только тогда вызывается другой объект-конструктор. Иногда вы будете в порядке, но много раз вы столкнетесь с неожиданным поведением.

Ответ 6

Похоже, что лучшим решением этой проблемы является просто создание другой функции для инициализации:

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

    Foo()
    {
        nullify();
    }

    Foo( int )
    {
        nullify();
    }
}

Ответ 7

Как говорили другие, это плохая идея и как возможный деструктивный случай: что делать, если вы делаете

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

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

не приветствуй землю бесконечной рекурсии.