为什么 C++ 复制构造函数在调用其子复制构造函数时行为不同

Why does the C++ copy constructor behave differently when calling its child copy constructor?

本文关键字:复制 构造函数 C++ 调用 为什么      更新时间:2023-10-16

我有以下玩具类A及其子类B:

#include <iostream>
using namespace std;
class A
{
    protected:
        int a;
    public:
        A():a(1){cout<<"A default constructor..."<<endl;}
        A(int i):a(i){cout<<"A non-default constructor..."<<endl;}
        A(const A &ao){cout<<"A copy constructor..."<<endl; a=ao.a;}
};
class B:public A
{
    private:
       int b;
    public:
       B(int i,int j):A(i),b(j){cout<<"B constructor..."<<endl;}
       //B(const B &bo){cout<<"B copy constructor... "<<endl; b=bo.b;}
       void print(){cout<<endl<<"class B, a: "<<a<<" b: "<<b<<endl<<endl;}
};
int main()
{
    B b1(3,8);
    b1.print();
    B b2=b1;
    b2.print();
}

我发现如果我不为 B 类提供复制构造函数,编译器将为我合成一个,而该编译器使用我为类 A 提供的复制构造函数。但是,如果我确实为类 B 提供了一个复制构造函数,其中我没有显式调用基类 A 的复制构造函数(请参阅代码),编译器将调用类 A 的默认构造函数?为什么?

这是

标准行为。这主要是为了一致性:任何未显式调用特定基类构造函数的用户定义构造函数都将调用默认构造函数。为什么复制构造函数会有所不同?

为派生类编写构造函数(任何构造函数)时,您可以(并且通常必须)在该构造函数的初始值设定项列表中显式初始化基类子对象。如果无法执行此操作,则编译器将隐式调用这些基类子对象的默认构造函数(假设它们可用)。此规则绝对适用于所有用户定义的构造函数。

这正是您案例中发生的事情。您忘记在 B::B(const B&) 的构造函数中初始化基A,因此默认构造函数用于该基。在这种情况下,B::B(const B&) 是一个复制构造函数这一事实没有任何区别。同样,对于所有类型的用户定义构造函数,它始终以这种方式工作。

现在,如果不提供用户定义的复制构造函数,则编译器将尝试隐式提供 on。隐式定义的复制构造函数将尝试调用所有基类的复制构造函数。语言规范只是说编译器提供的复制构造函数以这种方式运行,这就是您的"为什么"问题的答案。

初始化

规则的顺序适用于此处。只能通过提供自己的构造函数来取代该规则。

您没有注意到的是,成员数据的处理方式与基类数据相同:如果编译器提供复制构造函数,则通过复制构造初始化,但如果编写存根复制构造函数,则默认构造初始化。

通过实现复制构造函数,您告诉编译器您将决定如何初始化成员和基类。 如果您没有其他说明,这些将使用默认方法进行初始化。