Размещение нового для переноса на другой конструктор
Это безопасно? Я не использую никаких виртуальных функций в своей реальной реализации, но я склонен полагать, что даже если бы я был, это все равно было бы безопасно.
class Foo
{
Foo()
{
// initialize things
}
Foo( int )
{
new ( this ) Foo();
}
}
Ответы
Ответ 1
К моменту ввода открытой фигурной скобки конструктора Foo(int)
все члены класса имеют свой конструктор. Если затем принудительно вызвать другой конструктор с новым местом размещения, вы переписываете текущее состояние класса. Это в основном означает, что все члены имеют свои конструкторы, которые называются дважды - если что-то делает new
в своем конструкторе, вы пропускаете этот контент, и вы действительно действительно испортите вещи! Вы эффективно создаете два объекта, а деструкторы для членов первого объекта никогда не вызываются, поскольку второй объект перезаписывает память первого объекта.
Другими словами это БАД! Не делай этого!
Наиболее распространенным обходным решением является использование какой-либо функции инициализации и вызов этого из обоих конструкторов. Это не позволит вам инициализировать члены константы и другие, которые должны быть в списке инициализаторов.
Ответ 2
http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.3
Ответ 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);
}
}
не приветствуй землю бесконечной рекурсии.