包含其他对象的类的C ++隐式复制构造函数

时间:2009-11-27 18:41:17

标签: c++ contains composition default-copy-constructor

我知道如果你没有自己实现,编译器有时会提供一个默认的复制构造函数。我很困惑这个构造函数到底是做什么的。如果我有一个包含其他对象的类,其中没有一个具有声明的复制构造函数,那么行为是什么?例如,像这样的类:

class Foo {
  Bar bar;
};

class Bar {
  int i;
  Baz baz;
};

class Baz {
  int j;
};

现在,如果我这样做:

Foo f1;
Foo f2(f1);

默认的复制构造函数会做什么? Foo中编译器生成的复制构造函数是否会调用Bar中编译器生成的构造函数来复制bar,然后在Baz中调用编译器生成的复制构造函数?

5 个答案:

答案 0 :(得分:67)

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
  • 如果声明任何构造函数(包括副本),则编译器不会实现默认构造函数。
  • 如果声明了复制构造函数,那么编译器将不会生成一个。
  • 如果声明赋值运算符,则编译器不会生成一个。
  • 如果声明了析构函数,编译器将不会生成析构函数。

查看代码,生成以下副本构造函数:

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

答案 1 :(得分:11)

编译器提供了一个复制构造函数,除非你自己声明(注意:不是 define )。编译器生成的复制构造函数只是调用类的每个成员(以及每个基类)的复制构造函数。

赋值运算符和析构函数BTW也是如此。但是默认构造函数不同:只有在您不自己声明任何其他构造函数时,才由编译器提供。

答案 2 :(得分:2)

是的,编译器生成的复制构造函数按成员在包含类中声明的顺序执行成员复制。如果任何成员类型本身不提供复制构造函数,则无法生成包含类的可能复制构造函数。如果你可以决定使用一些适当的方法初始化不能复制构造的成员的值 - 也许可以通过使用其他构造函数之一,那么仍然可以手动编写一个。

答案 3 :(得分:0)

编译器将为您生成所需的构造函数。

但是,只要您自己定义一个复制构造函数,编译器就会放弃为该类生成任何内容,如果您没有定义适当的构造函数,它将给出错误。

使用您的示例:

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

尝试默认实例化或复制构造Foo会抛出错误,因为Baz不是可复制构造的,并且编译器无法为Foo生成默认和复制constroctor。

答案 4 :(得分:0)

C ++ default copy constructor创建shallow副本。浅拷贝不会创建原始对象可能引用的对象的新副本;旧对象和新对象将只包含指向同一内存位置的不同指针。