Это недавно подошло в классе, по которому я - обучающий помощник. Мы учили студентов, как сделать конструкторов копии в C++ и студентов, которым первоначально преподавали Java, который спрашивают, если можно вызвать одного конструктора от другого. Я знаю, что ответ на это не, поскольку они используют педантичный флаг для своего кода в классе, и старые стандарты не имеют поддержки этого. Я нашел на Stackoverflow и других сайтах, что предложение фальсифицировало это использование new (this)
такой следующим образом
class MyClass
{
private:
int * storedValue;
public:
MyClass(int initialValue = 0)
{
storedValue = new int(initialValue);
}
~ MyClass()
{
delete storedValue;
}
MyClass(const MyClass &b)
{
new (this) MyClass(*(b.storedValue));
}
int value() {
return *storedValue;
}
};
Это - действительно простой код и очевидно не сохраняет кода путем многократного использования конструктора, но это просто, например.
Мой вопрос состоит в том, если это даже стандартно совместимый, и если существуют какие-либо пограничные случаи, которые нужно рассмотреть, который препятствовал бы тому, чтобы это было звуковым кодом?
Править: Я должен отметить, что это кажется очень опасным для меня, но это больше от точки наблюдения, я действительно не понимаю это больше, чем знание, как это может разложиться. Я просто хотел удостовериться, что, если спрошено об этом студентами, что я могу направить их к тому, почему банка или не должна делать этого. Я уже предложил им для всех практических целей использовать общий метод инициализации. Это - больше обучающего вопроса, чем для практического проекта.
C ++ 0x представит синтаксис, позволяющий конструкторам вызывать другие конструкторы.
До тех пор new (this)
работает в некоторых случаях, но не во всех.В частности, попав в конструктор, ваши базовые классы уже полностью построены. Реконструкция с помощью new (this)
повторно вызывает базовые конструкторы без вызова базовых деструкторов, поэтому ожидайте проблем, если бы базовые классы не ожидали такого рода взлома - а они, вероятно, не ожидали.
Пример для ясности:
class Base
{
public:
char *ptr;
MyFile file;
std::vector vect;
Base()
{
ptr = new char[1000];
file.open("some_file");
}
~Base()
{
delete [] ptr;
file.close();
}
};
class Derived : Base
{
Derived(Foo foo)
{
}
Derived(Bar bar)
{
printf(ptr...); // ptr in base is already valid
new (this) Derived(bar.foo); // ptr re-allocated, original not deleted
//Base.file opened twice, not closed
// vect is who-knows-what
// etc
}
}
или, как говорится, «начинается веселье»
Вот что об этом говорится в FAQ по C ++ в вопросе : «Может ли один конструктор класса вызвать другой конструктор того же класса для инициализации этого объекта?»:
BTW НЕ пытайтесь добиться этого путем размещения нового. Некоторые люди думают, что могут сказать
new (this) Foo (x, int (x) +7)
в телеFoo :: Foo (char)
. Однако это плохо, плохо, плохо. Пожалуйста, не пишите мне и не говорите, что он кажется работает с вашей конкретной версией вашего конкретного компилятора; это плохо. Конструкторы делают кучу маленьких волшебных вещей за кулисами, но эта плохая техника наступает на частично сконструированные части. Просто сказать нет.
Поскольку этот точный код написан, он должен работать - хотя я не могу точно представить, зачем вы пишете такой код . В частности, это зависит от того факта, что весь указатель всегда используется только для ссылки на один int. В таком случае, почему они просто не поместили int в объект вместо использования указателя и динамического выделения int? Короче говоря, то, что у них есть, длинное и неэффективное, но существенно не отличается от:
class MyClass {
int v;
public:
MyClass(int init) : v(init) {}
int value() { return v; }
};
К сожалению, в ту минуту, когда вы пытаетесь получить какое-то реальное использование от указателя (например, выделяя разное количество памяти в разных объектах), появляется «трюк» они используют с размещением новых завершений работы - это полностью зависит от того факта, что каждый объект выделяет точно такой же объем памяти.Поскольку вы ограничены одним и тем же распределением в каждом, зачем помещать это выделение в кучу вместо того, чтобы делать его частью самого объекта?
По правде говоря, есть обстоятельства, когда это имело бы смысл. Единственное, что я могу сразу придумать, это то, что выделение велико и вы работаете в среде, где места в куче намного больше, чем в стеке.
Код работает, но он полезен только в довольно узких конкретных обстоятельствах. Это не кажется мне чем-то, что я рекомендовал бы в качестве примера того, как что-то делать.
Если вы не пытаетесь вызвать родительский конструктор, я бы предложил создать частный метод инициализации. Нет причин, по которым вы не можете вызвать общий инициализатор в своих конструкторах.
Члены и базовые классы будут инициализированы перед входом в тело конструктора, а затем снова инициализируются при вызове второго конструктора. В общем, это приведет к утечкам памяти и, возможно, неопределенному поведению.
Итак, ответ - «нет, это не звуковой код».
Это не работает, если у вас есть такой конструктор:
class MyClass {
public:
MyClass( const std::string & PathToFile )
: m_File( PathToFile.c_str( ) )
{
}
private:
std::ifstream m_File;
}
Исходный аргумент не может быть восстановлен, поэтому вы не можете вызвать этот конструктор из копирующего конструктора.