14.12.2 向上类型转换和拷贝构造函数

如果允许编译器为派生类生成拷贝构造函数,它将首先自动地调用基类的拷贝构造函数,然后再是各成员对象的拷贝构造函数(或者在内建类型上执行位拷贝),因此可以得到正确的操作:

14.12.2 向上类型转换和拷贝构造函数 - 图1

14.12.2 向上类型转换和拷贝构造函数 - 图2

从对其中Parent部分调用operator<<的方式可以看出,Child中的operator<<很有意思,它通过将Child对象类型转换为Parent&(但如果是类型转换了一个基类对象,而不是一个引用的话,将得不到所需要的结果):

14.12.2 向上类型转换和拷贝构造函数 - 图3

这时编译器把它当做一个Parent类型,将调用operator<<的Parent版本。

我们可以看到Child没有显式定义的拷贝构造函数。编译器将通过调用Parent和Member的拷贝构造函数来生成它的拷贝构造函数(如果我们没有创建任何构造函数,它是生成的四个函数之一,其他还有默认构造函数、operator=和析构函数)。这可从下面的输出中显示出来:

14.12.2 向上类型转换和拷贝构造函数 - 图4

然而,如果试着为Child写自己的拷贝构造函数,并且出现错误:

14.12.2 向上类型转换和拷贝构造函数 - 图5

这时将会为Child中的基类部分调用默认的构造函数,这是在没有其他的构造函数可供选择调用的情况下,编译器回溯搜索的结果(记住某些构造函数总是必须为每个对象所调用,而不管它是否是一个其他类的子对象)。这样输出将会是:

14.12.2 向上类型转换和拷贝构造函数 - 图6

这可能并不是我们所希望的,因为通常我们会希望基类部分从已存在对象拷贝至一个新的对象,以作为拷贝构造函数的一部分。

为了解决这个问题,必须记住无论何时我们在创建了自己的拷贝构造函数时,都要正确地调用基类拷贝构造函数(正如编译器所作的)。这乍一看可能有点奇怪,但它是向上类型转换的另一种情况:

14.12.2 向上类型转换和拷贝构造函数 - 图7

奇怪的部分在于调用Parent的拷贝构造函数的地方:Parent(c)。传送一个Child对象给Parent构造函数意味着什么?因为Child是由Parent继承而来,所以Child的引用也就相当于Parent的引用。基类拷贝构造函数的调用将一个Child的引用向上类型转换为一个Parent的引用,并且使用它来执行拷贝构造函数。当我们创建自己的拷贝构造函数时,也总会做同样的事情。