Списки инициализации конструктора копирования
Я знаю, что если вы выйдете из списка инициализации в конструкторе 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()?
}
Ответы
Ответ 1
Явно определенные конструкторы копий не вызывают конструкторы копирования для членов.
Когда вы вводите тело конструктора, каждый член этого класса будет инициализирован. То есть, как только вы дойдете до {
, вам гарантировано, что все ваши члены были инициализированы.
Если не указано, члены инициализируются по умолчанию в порядке, указанном в классе. (И если они не могут быть, программа плохо сформирована.) Поэтому, если вы определяете свои собственные копировать конструктор, теперь вам нужно называть любые конструкторы экземпляров-членов по своему желанию.
Вот небольшая программа, которую вы можете скопировать-вставить где-нибудь и обходиться с помощью:
#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);
}
As-is, это печатает:
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&)
Как вы можете видеть, как только вы явно объявляете конструктор-копию, вы несете ответственность за копирование всех членов класса; это ваш конструктор.
Это относится ко всем конструкторам, включая конструкторы перемещения.
Ответ 2
Да. Ctors - ctors.
Ответ 3
Для любой переменной-члена, имеющей конструктор по умолчанию, который конструктор по умолчанию вызывается, если вы явно не добавили какой-либо другой вызов конструктора для этой переменной-члена в список инициализации.
Ответ 4
Подробнее см.: Есть ли неявный конструктор по умолчанию в С++?
Short:
- Созданный компилятором "Конструктор по умолчанию": использует конструктор по умолчанию для каждого элемента.
- Созданный компилятором "Copy Constructor": использует конструктор копирования каждого элемента.
- Сгенерированный компилятором "Оператор присваивания": использует оператор присваивания для каждого элемента.
Ответ 5
Нет ничего волшебного в конструкторе копирования, кроме того, что компилятор добавит его, если потребуется. Но в том, как он работает, нет ничего особенного - если вы явно не говорите "использовать такой-то и такой конструктор", он будет использовать значение по умолчанию.
Ответ 6
Не в 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;
}
Ответ 7
В зависимости от того, как инициируется конструктор базового вызова, конструкторы-члены будут вызываться одинаково. Например, пусть начнется с:
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
Enjoy.
Ответ 8
Когда компилятор предоставляет код по умолчанию, что, по вашему мнению, компилятор делает для переменных-членов? Он копирует его.
В том же ключе, если указатель определяется пользователем, и если кто-то не учитывает некоторые члены, эти члены не могут быть оставлены неинициализированными. Во время строительства устанавливаются инварианты классов, которые необходимо постоянно поддерживать. Итак, компилятор делает это для вас.