派生类的初始化列表上的基类复制构造函数 (C++)

发布于 2024-10-04 00:51:38 字数 482 浏览 5 评论 0原文

举个例子:

class Base {
  Base (const Base & copyFrom) { globalRegister (* this); }
}

class Derived {
  Derived (const Derived & copyFrom) : Base (copyFrom) {}
}

我读过一些建议,将 Base 的复制构造函数包含在 Derived 的初始化列表中,以便复制 Base 的属性(如示例中所示)。

但是,我有 Base 的复制构造函数将其自身 (* this) 传递给其他对象(要向该对象注册)。在这种情况下,我实际上必须在 Derived 的复制构造函数的初始化列表上使用(隐式或显式)Base 的(默认)构造函数,并且仅在实际存在对象时在 Derived 的复制构造函数的主体中调用 Base 的复制构造函数可以通过 Base 的复制构造函数附加吗?否则 - (* this) 是一个有效的对象吗?

Let the example be:

class Base {
  Base (const Base & copyFrom) { globalRegister (* this); }
}

class Derived {
  Derived (const Derived & copyFrom) : Base (copyFrom) {}
}

I've read suggestions to include the Base's copy constructor on the initialisation list of Derived in order to copy over the Base's properties (as in the example).

However, I have the Base's copy constructor passing itself (* this) to other object (to be registered with that object). Would that be a case where I actually must use (implicitly or explicitly) Base's (default) constructor on the initialisation list of Derived's copy constructor, and call the Base's copy constructor only in the body of Derived's copy constructor, when there is actually an object that can be attached by Base's copy constructor? Else - is (* this) a valid object?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

栀子花开つ 2024-10-11 00:51:38

在这种情况下,我实际上必须在 Derived 的复制构造函数的初始化列表上使用(隐式或显式)Base 的(默认)构造函数,并且仅在 Derived 的复制构造函数的主体中调用 Base 的复制构造函数(当存在时)实际上是一个可以通过 Base 的复制构造函数附加的对象?

你到底为什么要这么做?
(哦,您不能从派生类的构造函数的主体调用基类的复制构造函数。只能从其初始化列表中调用。)

否则 - (* this) 是一个有效的对象吗?

当基类的初始化列表完成时,基类的所有成员(和基类)都已完全构造完毕。然而,类本身只有在其构造函数完成后才被完全构造。
更重要的是,派生类的构造函数还没有开始,因此该对象还不是派生类的对象。

因此,无论注册函数做什么,它都必须考虑到对象的动态类型是base并且其构造函数尚未完成。 (为了安全起见,它所能做的就是将对象的地址存储在某个地方。)

Would that be a case where I actually must use (implicitly or explicitly) Base's (default) constructor on the initialisation list of Derived's copy constructor, and call the Base's copy constructor only in the body of Derived's copy constructor, when there is actually an object that can be attached by Base's copy constructor?

Why on earth would you want to do that?
(Oh, and you can not call a base class' copy constructor from a derived class' constructor's body. Only from its initialization list.)

Else - is (* this) a valid object?

The moment the base's initialization list has completed, all of base's members (and base classes) are fully constructed. The class itself, however, is only fully constructed when its constructor has finished.
More importantly, the derived class' constructor hasn't even started yet, so the object is not a derived class' object yet.

So whatever that registering function does, it has to take into account that the object's dynamic type is base and that its constructor hasn't finished yet. (To be safe, all it can do is to store the object's address somewhere.)

薄荷港 2024-10-11 00:51:38

仅供参考,该行为由 C++03 的 § 12.7 2-3 指定:

2) 显式或隐式地将引用类 X 的对象的指针(左值)转换为指向 X 的直接或间接基类 B 的指针(引用),X 的构造以及所有直接或间接从 B 派生的直接或间接基类应已开始,并且这些类的销毁不应完成,否则转换会导致未定义的行为。

this 是一个指向Derived 的指针。在 Base::Base() 中,this 被隐式转换为 Base*,这是允许的,因为 Derived 的构造已经开始并且它没有从 Base 派生的其他碱基。

§ 12.7 2 继续:

要形成指向对象 obj 的直接非静态成员的指针(或访问其值), obj 的构造应已开始,且其销毁不应完成,否则指针值的计算(或访问成员值)导致未定义的行为。

最后,§ 12.7 3 也很重要:

3) 成员函数,包括虚函数 (10.3),可以在构造或销毁期间调用 (12.6.2)。当从构造函数(包括数据成员的 mem-initializer)或析构函数直接或间接调用虚拟函数时,并且调用适用的对象是正在构造的对象或销毁时,调用的函数是在构造函数或析构函数自己的类或其基类之一中定义的函数,但不是在从构造函数或析构函数的类派生的类中重写它的函数,也不是在另一个类中重写它的函数最派生对象的基类 (1.8)。如果虚拟函数调用使用显式类成员访问(5.2.5)并且对象表达式引用正在构造或销毁的对象,但其类型既不是构造函数或析构函数自己的类或其基类之一,则调用未定义。

这两个子句意味着,一旦 Base 构造函数开始,Derived 的实例就是一个成熟的 Base,尽管它可能处于不一致的状态。

Just for reference, the behavior is specified by § 12.7 2-3 of C++03:

2) To explicitly or implicitly convert a pointer (an lvalue) referring to an object of class X to a pointer (reference) to a direct or indirect base class B of X, the construction of X and the construction of all of its direct or indirect bases that directly or indirectly derive from B shall have started and the destruction of these classes shall not have completed, otherwise the conversion results in undefined behavior.

this is a pointer to Derived. In Base::Base(), this is implicitly cast to a Base*, which is allowed because the construction of Derived has started and it has no other bases that derive from Base.

§ 12.7 2 continues:

To form a pointer to (or access the value of) a direct nonstatic member of an object obj, the construction of obj shall have started and its destruction shall not have completed, otherwise the computation of the pointer value (or accessing the member value) results in undefined behavior.

Finally, § 12.7 3 is also important:

3) Member functions, including virtual functions (10.3), can be called during construction or destruction (12.6.2). When a virtual function is called directly or indirectly from a constructor (including from the mem-initializer for a data member) or from a destructor, and the object to which the call applies is the object under construction or destruction, the function called is the one defined in the constructor or destructor’s own class or in one of its bases, but not a function overriding it in a class derived from the con- structor or destructor’s class, or overriding it in one of the other base classes of the most derived object (1.8). If the virtual function call uses an explicit class member access (5.2.5) and the object-expression refers to the object under construction or destruction but its type is neither the constructor or destructor’s own class or one of its bases, the result of the call is undefined.

These two clauses means an instance of Derived is a fully-fledged Base once a Base constructor begins, though it might be in an inconsistent state.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文