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

StackOverflow https://stackoverflow.com/questions/1810163

Вопрос

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

class Foo {
  Bar bar;
};

class Bar {
  int i;
  Baz baz;
};

class Baz {
  int j;
};

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

Foo f1;
Foo f2(f1);

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

Это было полезно?

Решение

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

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

Деструктор

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
  • Если Любой объявляется конструктор (включая copy), тогда конструктор по умолчанию не реализуется компилятором.
  • Если объявлен конструктор копирования, то компилятор не будет его генерировать.
  • Если объявлен оператор присваивания, то компилятор не будет его генерировать.
  • Если объявлен деструктор, компилятор не будет его генерировать.

При просмотре вашего кода генерируются следующие конструкторы копирования:

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

Другие советы

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top