举个例子:
class Base {
Base (const Base & copyFrom) { globalRegister (* this); }
}
class Derived {
Derived (const Derived & copyFrom) : Base (copyFrom) {}
}
我已经阅读了建议,在Baseived的初始化列表中包含Base的复制构造函数,以便复制Base的属性(如示例所示)。
但是,我有Base的复制构造函数将自身(* this)传递给其他对象(要向该对象注册)。这是否真的必须在Derived的复制构造函数的初始化列表中使用(隐式或显式)Base(默认)构造函数,并且只在Derived的复制构造函数的主体中调用Base的复制构造函数,当实际存在对象时可以通过Base的复制构造函数附加吗?否则 - (* this)是一个有效的对象吗?
答案 0 :(得分:5)
这是否真的必须在Derived的复制构造函数的初始化列表中使用(隐式或显式)Base(默认)构造函数,并且只在Derived的复制构造函数的主体中调用Base的复制构造函数,当有实际上是一个可以通过Base的复制构造函数附加的对象?
为什么你想这样做?
(哦,你不能从派生类'构造函数的 body 中调用基类的拷贝构造函数。只能从它的初始化列表中调用。)
否则 - (* this)是一个有效的对象吗?
基础初始化列表完成的那一刻,所有基础成员(和基类)都是完全构造的。然而,类本身只有在构造函数完成时才完全构造 更重要的是,派生类的构造函数尚未启动,因此该对象尚未成为派生类的对象。
因此无论注册函数做什么,它都必须考虑对象的动态类型是base
并且其构造函数尚未完成。 (为了安全起见,它所能做的只是将对象的地址存储在某个地方。)
答案 1 :(得分:1)
仅供参考,行为由C ++ 03的§12.72-3指定:
2)显式或隐式地将引用类X的对象的指针(左值)转换为指向X的直接或间接基类B的指针(引用),构造X和所有的构造直接或间接从B派生的直接或间接基础应该已经开始,并且这些类的销毁不应该完成,否则转换会导致不确定的行为。
this
是指向Derived
的指针。在Base::Base()
中,this
被隐式转换为Base*
,这是允许的,因为Derived的构造已经开始,并且没有其他基础派生自Base
。
§12.72继续:
要形成一个指向(或访问其值)对象obj的直接非静态成员的指针,obj的构造应该已经开始并且它的销毁不应该已经完成,否则计算指针值(或访问成员值)导致未定义的行为。
最后,§12.73也很重要:
3)成员函数,包括虚函数(10.3),可以在构造或销毁期间调用(12.6.2)。当直接或间接从构造函数(包括来自 mem-initializer 的数据成员)或析构函数调用虚函数时,调用所适用的对象是正在构建的对象或destroy,被调用的函数是在构造函数或析构函数自己的类或其基础中定义的函数,但不是在从构造函数或析构函数的类派生的类中重写它的函数,或者在其中一个类中覆盖它的函数最派生对象的基类(1.8)。如果虚函数调用使用显式类成员访问(5.2.5)并且object-expression引用正在构造或销毁的对象但其类型既不是构造函数或析构函数自己的类或其基础之一,那么结果是电话未定义。
这两个子句意味着Derived
的实例在Base
构造函数开始时是完全成熟的Base
,尽管它可能处于不一致状态。