4

我有以下玩具类 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 的默认构造函数?这是为什么?

4

4 回答 4

9

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

于 2013-09-08T19:20:54.363 回答
1

当您为派生类编写构造函数(任何构造函数)时,您可以(并且通常必须)显式地初始化该构造函数的初始化列表中的基类子对象。如果你没有这样做,那么编译器将隐式调用这些基类子对象的默认构造函数,假设它们是可用的。此规则绝对适用于所有用户定义的构造函数。

这正是你的情况。您忘记AB::B(const B&)的构造函数中初始化 base ,因此该 base 使用了默认构造函数。B::B(const B&)在这种情况下,作为复制构造函数的事实没有任何区别。对于所有类型的用户定义的构造函数,它再次以这种方式工作。

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

于 2013-09-08T20:09:10.663 回答
0

初始化规则的顺序在这里适用。您只能通过提供自己的构造函数来取代该规则。

于 2013-09-08T20:08:54.893 回答
0

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

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

于 2013-09-08T21:14:28.297 回答