为啥隐式复制构造函数调用基类复制构造函数而定义的复制构造函数不调用?

Posted

技术标签:

【中文标题】为啥隐式复制构造函数调用基类复制构造函数而定义的复制构造函数不调用?【英文标题】:Why does the implicit copy constructor calls the base class copy constructor and the defined copy constructor doesn't?为什么隐式复制构造函数调用基类复制构造函数而定义的复制构造函数不调用? 【发布时间】:2012-02-28 23:54:08 【问题描述】:

考虑一个类层次结构,其中A 是基类,B 派生自A

如果B中没有定义拷贝构造函数,编译器会合成一个。当被调用时,这个复制构造函数将调用基类复制构造函数(即使是合成的,如果用户没有提供的话)。

#include <iostream>

class A 
    int a;
public:
    A() 
        std::cout << "A::Default constructor" << std::endl;
    

    A(const A& rhs) 
        std::cout << "A::Copy constructor" << std::endl;
    
;

class B : public A 
    int b;
public:
    B() 
        std::cout << "B::Default constructor" << std::endl;
    
;

int main(int argc, const char *argv[])

    std::cout << "Creating B" << std::endl;
    B b1;
    std::cout << "Creating B by copy" << std::endl;
    B b2(b1);
    return 0;

输出:

Creating B
A::Default constructor
B::Default constructor
Creating B by copy
A::Copy constructor

如果用户在B 中定义了自己的复制构造函数,则在调用时,此复制构造函数将调用基类默认构造函数,除非显式存在对基类复制构造函数的调用(例如在初始化列表中)。

#include <iostream>

class A 
    int a;
public:
    A() 
        std::cout << "A::Default constructor" << std::endl;
    

    A(const A& rhs) 
        std::cout << "A::Copy constructor" << std::endl;
    
;

class B : public A 
    int b;
public:
    B() 
        std::cout << "B::Default constructor" << std::endl;
    
    B(const B& rhs) 
        std::cout << "B::Copy constructor" << std::endl;
    
;

int main(int argc, const char *argv[])

    std::cout << "Creating B" << std::endl;
    B b1;
    std::cout << "Creating B by copy" << std::endl;
    B b2(b1);
    return 0;

输出:

Creating B
A::Default constructor
B::Default constructor
Creating B by copy
A::Default constructor
B::Copy constructor

我的问题是,为什么用户定义的复制构造函数不调用基类复制构造函数作为默认行为?

【问题讨论】:

如果默认情况下是这样,您将如何指定您不希望发生这种情况的情况? @PlasmaHH ParentClass() 在初始化列表中。我相信这仍然会非常不一致和令人困惑。 @MarkB:确实,我希望他在考虑时也能得出同样的结论…… 因为标准委员会有一个选择并且在我看来做出了最合乎逻辑的选择(如果您没有指定如何调用基类构造函数,那么您必须是指默认的那个(没有参数),因为您没有指定任何参数)。 还要注意同样的逻辑适用于所有成员以及基类。 【参考方案1】:

所有基子构造函数都调用父默认构造函数。这就是标准的定义方式。正如您所指出的,如果您希望派生类 B 调用 A 的复制构造函数,您必须明确要求它

#include <iostream>

class A 
int a;
public:
A() 
    std::cout << "A::Default constructor" << std::endl;


A(const A& rhs) 
    std::cout << "A::Copy constructor" << std::endl;

;

class B : public A 
int b;
public:
B() 
    std::cout << "B::Default constructor" << std::endl;

B(const B& rhs):A(rhs) 
    std::cout << "B::Copy constructor" << std::endl;

;

int main(int argc, const char *argv[])

std::cout << "Creating B" << std::endl;
B b1;
std::cout << "Creating B by copy" << std::endl;
B b2(b1);
return 0;

之所以如此,是因为编译器无法知道每个不同的构造函数应该调用哪个父构造函数,因此我们有 默认构造函数 对于所有其他构造函数,您必须明确声明它们。

输出:

Creating B
A::Default constructor
B::Default constructor
Creating B by copy
A::Copy constructor
B::Copy constructor

【讨论】:

All base child constructors call the parent default constructor.... 除了隐式复制构造函数:)! 隐式复制构造函数是编译器提供的最原始操作的显式名称,即源到目标的按位复制。 IMO 它只是一个花哨的名字。【参考方案2】:

简单(可能是陈词滥调)的答案是因为您没有告诉它。由于您正在编写派生的复制构造函数,因此您可以完全控制它的行为方式。未能指定对基类的调用,编译器通过调用基类默认构造函数生成代码以初始化基类。

【讨论】:

【参考方案3】:

这就是定义隐式复制构造函数的方式(调用默认值没有意义)。一旦定义了任何构造函数(复制或其他),它的正常自动行为就是调用默认父构造函数,因此为一个特定的用户定义构造函数更改它是不一致的。

【讨论】:

以上是关于为啥隐式复制构造函数调用基类复制构造函数而定义的复制构造函数不调用?的主要内容,如果未能解决你的问题,请参考以下文章

C++ 为啥基类/结构构造函数不能有多个参数可以从派生中隐式调用?

派生类在基类中删除时是不是会有隐式复制构造函数或赋值运算符?

为啥在我的代码中调用复制构造函数而不是移动构造函数?

如何从派生类复制构造函数调用基类复制构造函数? [复制]

为啥Java没有复制构造函数?

为啥这段代码试图调用复制构造函数?