Копировать списки инициализации конструктора

Я знаю, что если вы оставите член из списка инициализации в конструкторе no-arg, будет вызван конструктор по умолчанию этого члена.

также ли конструкторы копирования вызывают конструктор копирования членов или они также вызывают конструктор по умолчанию?

class myClass {
  private:
    someClass a;
    someOtherClass b;
  public:
    myClass() : a(DEFAULT_A) {} //implied is b()
    myClass(const myClass& mc) : a(mc.a) {} //implied is b(mc.b)??? or is it b()?
}

8 ответов


явно определенные конструкторы копирования не вызывают конструкторы копирования для членов.

когда вы вводите тело конструктора, каждый член этого класса будет инициализирован. То есть, как только вы доберетесь до { вы гарантируете, что все ваши члены были инициализированы.

если не указано, члены инициализируются по умолчанию в порядке их появления в классе. (и если они не могут быть, программа плохо сформированные.) Итак, если вы определяете собственный конструктор копирования, теперь вам нужно вызвать любые конструкторы копирования членов по желанию.

вот небольшая программа, которую вы можете скопировать-вставить где - нибудь и возиться с:

#include <iostream>

class Foo {
public:
    Foo() {
        std::cout << "In Foo::Foo()" << std::endl;
    }

    Foo(const Foo& rhs) {
        std::cout << "In Foo::Foo(const Foo&)" << std::endl;
    }
};

class Bar {
public:
    Bar() {
        std::cout << "In Bar::Bar()" << std::endl;
    }

    Bar(const Bar& rhs) {
        std::cout << "In Bar::Bar(const Bar&)" << std::endl;
    }
};

class Baz {
public:
    Foo foo;
    Bar bar;

    Baz() {
        std::cout << "In Baz::Baz()" << std::endl;
    }

    Baz(const Baz& rhs) {
        std::cout << "In Baz::Baz(const Baz&)" << std::endl;
    }
};

int main() {
    Baz baz1;
    std::cout << "Copying..." << std::endl;
    Baz baz2(baz1);
}

как есть, это отпечатки:

In Foo::Foo()
In Bar::Bar()
In Baz::Baz()
Copying...
In Foo::Foo()
In Bar::Bar()
In Baz::Baz(const Baz&)

обратите внимание, что это по умолчанию-инициализация членов Baz.

комментируя явный конструктор копирования, например:

/*
Baz(const Baz& rhs) {
    std::cout << "In Baz::Baz(const Baz&)" << std::endl;
}
*/

вывод станет следующим:

In Foo::Foo()
In Bar::Bar()
In Baz::Baz()
Copying...
In Foo::Foo(const Foo&)
In Bar::Bar(const Bar&)

он называет копия-конструктор на обоих.

а если мы снова введем Bazконструктор копирования и явное копирование одного члена:

Baz(const Baz& rhs) :
    foo(rhs.foo)
{
    std::cout << "In Baz::Baz(const Baz&)" << std::endl;
}

получаем:

In Foo::Foo()
In Bar::Bar()
In Baz::Baz()
Copying...
In Foo::Foo(const Foo&)
In Bar::Bar()
In Baz::Baz(const Baz&)

как вы можете видеть, как только вы явно объявите copy-constructor вы отвечают за копирование всех членов класса; теперь это ваш конструктор.

это относится ко всем конструкторам, включая конструкторы перемещения.


да. Ctors - это ctors.


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


Подробнее см.: есть неявный конструктор по умолчанию в C++?

коротко:

  • компилятор сгенерировал "конструктор по умолчанию": использует конструктор по умолчанию каждого члена.
  • компилятор сгенерировал "конструктор копирования": использует конструктор копирования каждого члена.
  • компилятор сгенерировал "оператор присваивания": использует оператор присваивания каждого члена.

в конструкторе копирования нет ничего волшебного, кроме того, что компилятор добавит его в случае необходимости. Но в том, как он фактически работает, нет ничего особенного - если вы явно не скажете "использовать такой-то конструктор", он будет использовать значение по умолчанию.


Не в VC9. Не уверен насчет остальных.

// compiled as: cl /EHsc contest.cpp
//
//    Output was:
//    Child1()
//    -----
//    Child1()
//    Child2()
//    Parent()
//    -----
//    Child1(Child1&)
//    Child2()
//    Parent(Parent&)

#include <cstdio>

class Child1 {
    int x;
public:
    static Child1 DEFAULT;

    Child1(){
        x = 0;
        printf("Child1()\n");
    }

    Child1(Child1 &other){
        x = other.x;
        printf("Child1(Child1&)\n");
    }
};

Child1 Child1::DEFAULT;

class Child2 {
    int x;
public:
    Child2(){
        x = 0;
        printf("Child2()\n");
    }

    Child2(Child2 &other){
        x = other.x;
        printf("Child2(Child2&)\n");
    }
};

class Parent {
    int x;
    Child1 c1;
    Child2 c2;

public:
    Parent(){
        printf("Parent()\n");
    }

    Parent(Parent &other) : c1(Child1::DEFAULT) {
        printf("Parent(Parent&)\n");
    }
};

int main(){
    printf("-----\n");
    Parent p1;
    printf("-----\n");
    Parent p2(p1);

    return 0;
}

В зависимости от того, как инициируется конструктор базового вызова, конструкторы элемента будут вызываться одинаково. Например, давайте начнем с:

struct ABC{
    int a;
    ABC() : a(0)    {   printf("Default Constructor Called %d\n", a);   };

    ABC(ABC  & other )  
    {
        a=other.a;
        printf("Copy constructor Called %d \n" , a ) ;
    };
};

struct ABCDaddy{
    ABC abcchild;
};

вы можете сделать эти тесты:

printf("\n\nTest two, where ABC is a member of another structure\n" );
ABCDaddy aD;
aD.abcchild.a=2;

printf( "\n Test: ABCDaddy bD=aD;  \n" );
ABCDaddy bD=aD; // Does call the copy constructor of the members of the structure ABCDaddy ( ie. the copy constructor of ABC is  called)

printf( "\n Test: ABCDaddy cD(aD); \n" );
ABCDaddy cD(aD);    // Does call the copy constructor of the members of the structure ABCDaddy ( ie. the copy constructor of ABC is  called)

printf( "\n Test: ABCDaddy eD; eD=aD;  \n" );
ABCDaddy eD;
eD=aD;          // Does NOT call the copy constructor of the members of the structure ABCDaddy ( ie. the copy constructor of ABC is not called)

выход:

Default Constructor Called 0

Test: ABCDaddy bD=aD;
Copy constructor Called 2

Test: ABCDaddy cD(aD);
Copy constructor Called 2

Test: ABCDaddy eD; eD=aD;
Default Constructor Called 0

наслаждайтесь.


когда компилятор предоставляет cctor по умолчанию, как вы думаете, что компилятор делает для переменных-членов? Это копировать конструкции его.

в том же ключе, если cctor определен пользователем, и если один оставляет некоторые члены, эти члены не могут быть оставлены неинициализированными. Инварианты классов устанавливаются во время построения и должны постоянно поддерживаться. Итак, компилятор делает это за вас.