复制构造函数初始化列表

时间:2009-04-16 04:06:49

标签: c++ copy-constructor

我知道如果你在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 个答案:

答案 0 :(得分:26)

显式定义的复制构造函数不会为成员调用复制构造函数。

当您输入构造函数的主体时,将初始化该类的每个成员。也就是说,一旦到达{,您就可以保证所有成员都已初始化。

除非另有说明,否则会按照它们在班级中出现的顺序对成员进行默认初始化。(如果不能,则表示程序格式不正确。)因此,如果您定义自己的复制构造函数,现在可以根据需要调用任何成员复制构造函数。

这是一个小程序,你可以复制粘贴到某处并乱用:

#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&)

如您所见,一旦您明确声明了复制构造函数,您负责复制所有类成员;它现在是你的构造函数。

这适用于所有构造函数,包括移动构造函数。

答案 1 :(得分:2)

是。 Ctors是ctors。

答案 2 :(得分:2)

对于具有默认构造函数的任何成员变量,如果尚未将该成员变量的任何其他构造函数调用显式添加到初始化列表中,则调用默认构造函数。

答案 3 :(得分:2)

有关详细信息,请参阅:Is there an implicit default constructor in C++?

短:

  • 编译器生成的“默认构造函数”:使用每个成员的默认构造函数。
  • 编译器生成的“复制构造函数”:使用每个成员的复制构造函数。
  • 编译器生成的“分配运算符”:使用每个成员的赋值运算符。

答案 4 :(得分:1)

复制构造函数没有什么神奇之处,除了编译器将在需要时添加它。但是在它实际运行的方式上,没有什么特别的 - 如果你没有明确说“使用这样的构造函数”,它将使用默认值。

答案 5 :(得分:1)

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

答案 6 :(得分:1)

根据基本调用构造函数的启动方式,将以相同的方式调用成员的构造函数。例如,让我们从:

开始
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

享受。

答案 7 :(得分:0)

当编译器提供默认cctor时,您认为编译器对成员变量做了什么?它复制构造它。

同样,如果cctor是用户定义的,如果一个成员遗漏了一些成员,那么这些成员就不能保持未初始化状态。不变量是在施工期间建立的,必须不断维护。所以,编译器会为你做这件事。