Используя новый (это) конструкторам повторного использования

Это недавно подошло в классе, по которому я - обучающий помощник. Мы учили студентов, как сделать конструкторов копии в 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;
        }
};

Это - действительно простой код и очевидно не сохраняет кода путем многократного использования конструктора, но это просто, например.

Мой вопрос состоит в том, если это даже стандартно совместимый, и если существуют какие-либо пограничные случаи, которые нужно рассмотреть, который препятствовал бы тому, чтобы это было звуковым кодом?

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

19
задан Manoj Patil 23 August 2019 в 10:50
поделиться

6 ответов

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
    }
}

или, как говорится, «начинается веселье»

17
ответ дан 30 November 2019 в 03:29
поделиться

Вот что об этом говорится в FAQ по C ++ в вопросе : «Может ли один конструктор класса вызвать другой конструктор того же класса для инициализации этого объекта?»:

BTW НЕ пытайтесь добиться этого путем размещения нового. Некоторые люди думают, что могут сказать new (this) Foo (x, int (x) +7) в теле Foo :: Foo (char) . Однако это плохо, плохо, плохо. Пожалуйста, не пишите мне и не говорите, что он кажется работает с вашей конкретной версией вашего конкретного компилятора; это плохо. Конструкторы делают кучу маленьких волшебных вещей за кулисами, но эта плохая техника наступает на частично сконструированные части. Просто сказать нет.

5
ответ дан 30 November 2019 в 03:29
поделиться

Поскольку этот точный код написан, он должен работать - хотя я не могу точно представить, зачем вы пишете такой код . В частности, это зависит от того факта, что весь указатель всегда используется только для ссылки на один int. В таком случае, почему они просто не поместили int в объект вместо использования указателя и динамического выделения int? Короче говоря, то, что у них есть, длинное и неэффективное, но существенно не отличается от:

class MyClass {
    int v;
public:
    MyClass(int init) : v(init) {}
    int value() { return v; }
};

К сожалению, в ту минуту, когда вы пытаетесь получить какое-то реальное использование от указателя (например, выделяя разное количество памяти в разных объектах), появляется «трюк» они используют с размещением новых завершений работы - это полностью зависит от того факта, что каждый объект выделяет точно такой же объем памяти.Поскольку вы ограничены одним и тем же распределением в каждом, зачем помещать это выделение в кучу вместо того, чтобы делать его частью самого объекта?

По правде говоря, есть обстоятельства, когда это имело бы смысл. Единственное, что я могу сразу придумать, это то, что выделение велико и вы работаете в среде, где места в куче намного больше, чем в стеке.

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

1
ответ дан 30 November 2019 в 03:29
поделиться

Если вы не пытаетесь вызвать родительский конструктор, я бы предложил создать частный метод инициализации. Нет причин, по которым вы не можете вызвать общий инициализатор в своих конструкторах.

3
ответ дан 30 November 2019 в 03:29
поделиться

Члены и базовые классы будут инициализированы перед входом в тело конструктора, а затем снова инициализируются при вызове второго конструктора. В общем, это приведет к утечкам памяти и, возможно, неопределенному поведению.

Итак, ответ - «нет, это не звуковой код».

11
ответ дан 30 November 2019 в 03:29
поделиться

Это не работает, если у вас есть такой конструктор:

class MyClass {
public:
    MyClass( const std::string & PathToFile )
    : m_File( PathToFile.c_str( ) )
    {
    }
private:
    std::ifstream m_File;
}

Исходный аргумент не может быть восстановлен, поэтому вы не можете вызвать этот конструктор из копирующего конструктора.

1
ответ дан 30 November 2019 в 03:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: