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

发布于 2024-08-12 22:13:29 字数 424 浏览 7 评论 0原文

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

class Foo {
  Bar bar;
};

class Bar {
  int i;
  Baz baz;
};

class Baz {
  int j;
};

现在如果我这样做:

Foo f1;
Foo f2(f1);

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

I know that the compiler sometimes provides a default copy constructor if you don't implement yourself. I am confused about what exactly this constructor does. If I have a class that contains other objects, none of which have a declared copy constructor, what will the behavior be? For example, a class like this:

class Foo {
  Bar bar;
};

class Bar {
  int i;
  Baz baz;
};

class Baz {
  int j;
};

Now if I do this:

Foo f1;
Foo f2(f1);

What will the default copy constructor do? Will the compiler-generated copy constructor in Foo call the compiler-generated constructor in Bar to copy over bar, which will then call the compiler-generated copy constructor in Baz?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

何以心动 2024-08-19 22:13:30

C++ 默认复制构造函数 创建一个 复制。浅拷贝不会创建原始对象可能引用的对象的新副本;旧对象和新对象将仅包含指向同一内存位置的不同指针。

The C++ default copy constructor creates a shallow copy. A shallow copy will not create new copies of objects that your original object may reference; the old and new objects will simply contain distinct pointers to the same memory location.

江心雾 2024-08-19 22:13:30

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

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

使用您的示例:

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

尝试默认实例化或复制构造 Foo 将引发错误,因为 Baz 不可复制构造,并且编译器无法生成 Foo 的默认和复制构造函数。

The compiler will generate the needed constructors for you.

However, as soon as you define a copy-constructor yourself, the compiler gives up generating anything for that class and will give and error if you don't have the appropriate constructors defined.

Using your example:

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

Trying to default instantiate or copy-construct Foo will throw an error since Baz is not copy-constructable and the compiler can't generate the default and copy constroctor for Foo.

空城仅有旧梦在 2024-08-19 22:13:29
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 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)
{}
Foo f1;
Foo f2(f1);

Yes this will do what you expect it to:
The f2 copy constructor Foo::Foo(Foo const&) is called.
This copy constructs its base class and then each member (recursively)

If you define a class like this:

class X: public Y
{
    private:
        int     m_a;
        char*   m_b;
        Z       m_c;
};

The following methods will be defined by your compiler.

  • Constructor (default) (2 versions)
  • Constructor (Copy)
  • Destructor (default)
  • Assignment operator

Constructor: Default:

There are actually two default constructors.
One is used for zero-initialization while the other is used for value-initialization. The used depends on whether you use () during initialization or not.

// 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'
{
}

Notes: If the base class or any members do not have a valid visible default constructor then the default constructor can not be generated. This is not an error unless your code tries to use the default constructor (then only a compile time error).

Constructor (Copy)

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

Notes: If the base class or any members do not have a valid visible copy constructor then the copy constructor can not be generated. This is not an error unless your code tries to use the copy constructor (then only a compile time error).

Assignment Operator

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

Notes: If the base class or any members do not have a valid viable assignment operator then the assignment operator can not be generated. This is not an error unless your code tries to use the assignment operator (then only a compile time error).

Destructor

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
  • If any constructor (including copy) is declared then the default constructor is not implemented by the compiler.
  • If the copy constructor is declared then the compiler will not generate one.
  • If the assignment operator is declared then the compiler will not generate one.
  • If a destructor is declared the compiler will not generate one.

Looking at your code the following copy constructors are generated:

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)
{}
柳若烟 2024-08-19 22:13:29

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

顺便说一句,赋值运算符和析构函数也是如此。不过,默认构造函数是不同的:仅当您自己不声明任何其他构造函数时,编译器才会提供默认构造函数。

The compiler provides a copy constructor unless you declare (note: not define) one yourself. The compiler-generated copy constructor simply calls the copy constructor of each member of the class (and of each base class).

The very same is true for the assignment operator and the destructor, BTW. It is different for the default constructor, though: That is provided by the compiler only if you do not declare any other constructor yourself.

暖伴 2024-08-19 22:13:29

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

Yes, the compiler-generated copy constructor performs a member-wise copy, in the order in which the members are declared in the containing class. If any of the member types do not themselves offer a copy constructor, the would-be copy constructor of the containing class cannot be generated. It may still be possible to write one manually, if you can decide on some appropriate means to initialize the value of the member that can't be copy-constructed -- perhaps by using one of its other constructors.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文