Подтвердить что ты не робот

С++ неявный конструктор копирования для класса, который содержит другие объекты

Я знаю, что компилятор иногда предоставляет конструктор копии по умолчанию, если вы не реализуете себя. Я смущен тем, что именно делает этот конструктор. Если у меня есть класс, который содержит другие объекты, ни один из которых не имеет объявленного конструктора копирования, каково будет поведение? Например, класс вроде этого:

class Foo {
  Bar bar;
};

class Bar {
  int i;
  Baz baz;
};

class Baz {
  int j;
};

Теперь, если я это сделаю:

Foo f1;
Foo f2(f1);

Что будет делать конструктор копии по умолчанию? Составитель, созданный компилятором в Foo, вызовет конструктор, сгенерированный компилятором, в Bar для копирования поверх Bar, который затем вызовет созданный компилятором конструктор копий в Baz?

4b9b3361

Ответ 1

Foo f1;
Foo f2(f1);

Да, это будет делать то, что вы ожидаете от этого:
Вызывается конструктор копирования f2 Foo:: Foo (Foo const &).
Эта копия создает свой базовый класс, а затем каждый член (рекурсивно)

Если вы определяете класс следующим образом:

class X: public Y
{
    private:
        int     m_a;
        char*   m_b;
        Z       m_c;
};

В вашем компиляторе будут определены следующие методы.

  • Конструктор (по умолчанию) (2 версии)
  • Конструктор (копирование)
  • Деструктор (по умолчанию)
  • Оператор присваивания

Конструктор: по умолчанию:

На самом деле существуют два стандартных конструктора.
Один используется для zero-initialization, а другой используется для value-initialization. Используется в зависимости от того, используете ли вы () во время инициализации или нет.

// Zero-Initialization compiler generated constructor
X::X()
    :Y()                // Calls the base constructor
                        //     If this is compiler generated use 
                        //     the `Zero-Initialization version'
    ,m_a(0)             // Default construction of basic PODS zeros them
    ,m_b(0)             // 
    m_c()               // Calls the default constructor of Z
                        //     If this is compiler generated use 
                        //     the `Zero-Initialization version'
{
}

// Value-Initialization compiler generated constructor
X::X()
    :Y()                // Calls the base constructor
                        //     If this is compiler generated use 
                        //     the `Value-Initialization version'
    //,m_a()            // Default construction of basic PODS does nothing
    //,m_b()            // The values are un-initialized.
    m_c()               // Calls the default constructor of Z
                        //     If this is compiler generated use 
                        //     the `Value-Initialization version'
{
}

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

Конструктор (копирование)

X::X(X const& copy)
    :Y(copy)            // Calls the base copy constructor
    ,m_a(copy.m_a)      // Calls each members copy constructor
    ,m_b(copy.m_b)
    ,m_c(copy.m_c)
{}

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

Оператор присваивания

X& operator=(X const& copy)
{
    Y::operator=(copy); // Calls the base assignment operator
    m_a = copy.m_a;     // Calls each members assignment operator
    m_b = copy.m_b;
    m_c = copy.m_c;

    return *this;
}

Примечания. Если базовый класс или любые члены не имеют действительного жизнеспособного оператора присваивания, то оператор присваивания не может быть сгенерирован. Это не ошибка, если ваш код не пытается использовать оператор присваивания (тогда это только ошибка времени компиляции).

Destructor

X::~X()
{
                        // First runs the destructor code
}
    // This is psudo code.
    // But the equiv of this code happens in every destructor
    m_c.~Z();           // Calls the destructor for each member
    // m_b              // PODs and pointers destructors do nothing
    // m_a          
    ~Y();               // Call the base class destructor
  • Если объявлен любой конструктор (включая копию), конструктор по умолчанию не реализуется компилятором.
  • Если конструктор копирования объявлен, компилятор не будет генерировать его.
  • Если оператор присваивания объявлен, то компилятор не будет генерировать его.
  • Если объявлен деструктор, компилятор не будет генерировать его.

Глядя на ваш код, создаются следующие конструкторы копирования:

Foo::Foo(Foo const& copy)
    :bar(copy.bar)
{}

Bar::Bar(Bar const& copy)
    :i(copy.i)
    ,baz(copy.baz)
{}

Baz::Baz(Baz const& copy)
    :j(copy.j)
{}

Ответ 2

Компилятор предоставляет конструктор копирования, если вы не объявите (обратите внимание: не определяете) один. Создатель, созданный компилятором, просто вызывает конструктор копирования каждого члена класса (и каждого базового класса).

То же самое верно для оператора присваивания и деструктора BTW. Однако он отличается от конструктора по умолчанию: это обеспечивается компилятором только в том случае, если вы не объявляете какой-либо другой конструктор самостоятельно.

Ответ 3

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

Ответ 4

Компилятор создаст для вас необходимые конструкторы.

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

Используя ваш пример:

class Baz {
    Baz(const Baz& b) {}
    int j;
};
class Bar {
    int i;
    Baz baz;
};
class Foo {
    Bar bar;
};

Попытка создать экземпляр или копию-конструкцию по умолчанию Foo вызовет ошибку, так как Baz не копируется и компилятор не может создать по умолчанию и copy constroctor для Foo.

Ответ 5

С++ конструктор копирования по умолчанию создает shallow копия. Неглубокая копия не создаст новых копий объектов, которые может ссылаться на ваш исходный объект; старые и новые объекты будут просто содержать разные указатели на одну и ту же ячейку памяти.