【问题标题】:C++ Inheritance of copy, move, swap, assignment and destructor? Which do I needC++复制、移动、交换、赋值和析构函数的继承?我需要哪个
【发布时间】:2014-01-02 20:48:35
【问题描述】:

假设我有两个班级

Base 管理一些内存。它具有工作移动、交换、分配和析构函数。 Derived 不会添加任何需要管理的新内容(没有新的内存分配)。

class Base
{
public:
    Base();
    Base(const Base& other);
    friend void swap(Base& a, Base& b);
    Base(Base&& other);

protected:
    int** some2Darray;
    int w, h;
};

class Derived : public Base
{
public:
    Derived();
    //...?
};

我是否需要在派生类中实现所有这些功能才能让它变得更好?如何重用基类中的这些功能?我不需要在这个类中管理更多内存。

如果我将成员添加到 Derived 类,这些函数会是什么样子?我应该完全重写所有这些函数还是有一些方法可以使用例如“复制”基类并在复制构造函数中额外复制一个添加的成员?

【问题讨论】:

  • 一定要继承,组合不行吗?如果您只想让Derived 能够访问由Base 管理的资源,您只需将Base 成员添加到Derived
  • 这看起来很经典XY question

标签: c++ inheritance


【解决方案1】:

我不确定移动运算符,但您不必实现复制 ctor、析构函数和复制运算符,因为标准函数会自动从所有基类调用相应的函数。

编辑:另见How to use base class's constructors and assignment operator in C++?

【讨论】:

    【解决方案2】:

    你可以继承(编辑:是的,这不是真正的继承,也许这应该明确指出)构造函数自c++11。通过

    class Derived : public Base
    {
    public:
        Derived();
        using Base::Base; // <-- this will import constructors
    };
    

    但这不会处理任何额外的事情!

    但是,您不需要复制代码。你可以只调用父函数。

    例如:

    class Derived : public Base
    {
       int extra;
    public:
       Derived() : Base(), extra(42){};
       Derived(const Derived& other) : Base(other) {extra = other.extra;};
       void copy(const Derived& other);
       friend void swap(Derived& a, Derived& b);
    };
    
    void Derived::copy(const Derived& other){
       Base::copy(other);
       extra = other.extra;
    }
    

    也不要忘记虚拟析构函数。

    编辑: 对于交换,我只需将派生实例转换为它们的基础,以使编译器使用为父类型定义的交换。然后交换额外的东西。

    void swap(Derived& a, Derived& b){
        swap(static_cast<Base&>(a), static_cast<Base&>(b));
        swap(a.extra, b.extra);
    }
    

    【讨论】:

    • 谢谢。这就是我要找的。还有一件事。如果派生中没有任何额外成员,我是否必须声明复制构造函数?或者我可以相信编译器从基类调用一个?
    • 再提一个问题,我会接受的。派生类中的交换函数呢?我有交换功能作为朋友功能。如何复用base中的swap函数?
    • @Hooch 好的。我一开始误解了,所以我不得不删除我的评论。再次首先抱歉没有回答第一个问题,但在我回答之前,Matthieu 的回答已经详细介绍了。我编辑了答案。我认为这应该可行。
    【解决方案3】:

    首先:构造函数、赋值运算符和析构函数不是继承的 (*)。相反,在某些情况下,它们可能会由编译器自动为您合成

    那么,什么时候需要写它们呢?只有当default生成的版本不符合你的需求时:

    • 可访问性不是您想要的(总是public
    • 方法应该是deleted
    • default 行为不正确(例如浅拷贝)
    • 编译器无法为您合成方法

    关于后两点:

    • 三法则规定,如果您编写复制构造函数、复制赋值运算符或析构函数中的任何一个;你也应该提供另外两个
    • 在 C++11 中,如果您编写了这 3 个特殊方法中的任何一个,则移动构造函数和移动赋值运算符不会自动合成
    • 在 C++11 中,如果您编写移动构造函数或移动赋值运算符,那么这 3 个特殊方法都不会自动合成

    (*) 名为继承构造函数的 C++11 特性名不副实,它比继承更具委派。 p>


    话虽如此,如果Derived 没有任何棘手的属性,那么您可能可以避免编写这些成员。如果您仍然希望编写它们(例如避免内联),您应该能够使用 = default 语法:

    // Derived.h
    class Derived: public Base {
    public:
        Derived(Derived const&) = default;
        Derived& operator(Derived const&);
    };
    
    // Derived.cpp
    Derived& Derived::operator=(Derived const&) = default;
    

    【讨论】:

    • 那么 Derived(Derived const&) = default;会从 Base 调用复制构造函数吗?
    • @Hooch: 是的,以及Derived 的每个其他基础和属性的复制构造函数,按照它们的声明顺序。
    猜你喜欢
    • 1970-01-01
    • 2020-12-15
    • 1970-01-01
    • 2018-10-31
    • 1970-01-01
    • 2016-09-13
    • 1970-01-01
    • 2015-11-16
    • 2012-11-14
    相关资源
    最近更新 更多