【问题标题】:C++ Should I overwrite copy constructor in derived classC++ 我应该覆盖派生类中的复制构造函数吗
【发布时间】:2014-01-01 12:57:06
【问题描述】:

我有一个带有自定义复制构造函数的基类,负责处理分配的内存。 我也有从那个基础派生的课程。该派生类不再使用内存。

复制构造函数在派生类中应该如何看待? 覆盖swap、move和assign怎么样?

【问题讨论】:

  • 忽略我之前的回答,编译器生成的复制构造函数是不正确的。
  • 请注意,在 C++(03) 中,构造函数是继承的。在每个子类中,您都是从零开始的。
  • 我想说继承不适用于构造函数,因为它们在逻辑上属于类,而不是对象(与析构函数相反)。
  • @JohnDibling 这对复制构造函数有多重要?基类的copy-ctor从派生类的隐式定义的copy-ctor中调用。
  • @DyP:阅读这个问题,我怀疑 OP 可能没有意识到他必须在派生类的初始化列表中显式调用基类的复制构造函数。我只是指出这一点。

标签: c++ class inheritance c++11


【解决方案1】:

除了swap 之外,您不应该做任何事情。基类在这方面就像一个数据成员,编译器生成的复制构造函数和赋值(如果适用,还会移动)包含它。

swap 不同,因为没有编译器生成的swap,只有命名空间std 中的默认实现,然后是程序员为用户定义的类型定义的任何特化或ADL 重载。

所以如果已经有一个看起来像这样的函数:

namespace foo {
    void swap(Base &lhs, Base &rhs) {
        lhs.swap(rhs); // or maybe the gory details are in this function
    }
}

你什么也不做,有些用户这样称呼它:

foo::swap(derived1, derived2);

然后基础部分将被交换,而派生部分不会,这很糟糕。

用户不应该那样调用 swap,但你知道用户是什么样的。如果BaseDerived 在同一个命名空间中,用户可能会觉得特别诱人,并且他们只是假设这一切都会奏效。因此,如果您认为您的用户(或您自己的代码库)在调用 swap 时没有原则,您可能需要确保 Derived 过载以阻止它们。

用户应致电swap 喜欢:

using std::swap;
swap(base1, base2);
swap(derived1, derived2);

当与Base 一起使用时将调用foo::swap,当与Derived 一起调用时将调用std::swapstd::swap<Derived> 可能效率低下(如果 Base 实现了交换以提高效率,那么 Derived 也应该这样做)。因此,出于这个原因,您可能也想为Derived 实现swap,但慢并不像错误那么糟糕。

也就是说,您是在询问动作。如果Derived 可以有效地移动,那么std::swap<Derived> 也不错。也许它可以改进,也许不能,但它应该是微不足道的,因为三步很难击败。

您和用户还应该知道swap 不能很好地处理多态性(因为通常两个对象必须具有相同的完整类型才能使它们交换才有意义),并且为基类实现它可能惹麻烦。

Derived d1, d2;
Base &b1 = &d1, &b2 = d2;
swap(b1, d2); // swaps just the base part
swap(b1, b2); // swaps just the base part

首先,用户需要知道不要那样调用swap。其次,您可能需要考虑 Baseswap 是否真的被精心设计为基类。

【讨论】:

  • “其次,您可能需要考虑带交换的 Base 作为基类是否真的设计得很好。” 一个很好的提示。但我认为这只适用于继承是公开的。
  • @DyP:是的,如果继承是私有的,那么也不会有有人调用错误的foo::swap 的危险。就大多数(所有?)实际目的而言,就类的用户而言,私有继承不是多态性。
【解决方案2】:

有所谓的五规则(以前的三规则),它表示您是否有用户定义的其中任何一个:

  • 复制构造函数
  • 移动构造函数
  • 析构函数
  • 复制赋值运算符
  • 移动赋值运算符

您可能还想用户定义其他四个。


另一方面,rule-of-zero:您可以使用自动处理这些事情的资源对象 (RAII),而不是定义这五个特殊的成员函数。

不管这样的对象是某个类C的数据成员还是基类(子对象):编译器提供的C隐式声明和定义的特殊成员函数会调用特殊成员这个资源对象的函数,这很可能就足够了。

提示:具体的例子可以给出更准确的答案

【讨论】:

    【解决方案3】:

    您不能“覆盖”构造函数。它不是 C++ 词汇表中的术语。您可以覆盖一个函数,但前提是它是虚拟的。

    通常你不应该在任何正确编写的基类的子类的构造函数中做任何特殊的事情。让每一代人都照顾好自己。

    【讨论】:

    • @JohnDibling 当然你可以在类中重载构造函数,就像任何其他函数一样。
    • 呃,哎呀。 :) 我只能说。
    猜你喜欢
    • 1970-01-01
    • 2011-03-17
    • 1970-01-01
    • 2020-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-07
    相关资源
    最近更新 更多