我应该避免多重实现继承吗?

时间:2018-10-29 15:01:04

标签: c++ inheritance multiple-inheritance

我想为可以从外部方法/类访问的类的不同属性定义接口。

  • 我使用了多个实现继承(=来自与接口继承相对的具体类的继承),这被认为是一种不好的做法(point 3 from here)。

  • 我将继承用于代码重用。如here所述,最好使用组合而不是继承来进行代码重用。但是有了组合,我所有的MyObj1,MyObj2,...,MyObj1000都将具有相同的get和set方法,而我几乎很少需要重新实现它们。

这种情况是否完美地说明了以下情况:当代码重用的多个实现继承是好的情况,或者此代码完全错误,我应该使用一些巧妙的语法/模式来避免这种设计? 我问这个问题是因为我相信后者。

#include <string>
#include <iostream>

// interface of object with name
class BaseName {
public:
    virtual void setName(const std::string& val) { name = val; }
    virtual std::string getName() const { return name; }
private:
    std::string name;
};

// user of interface of objects with name
void nameUser(BaseName* b) {
    std::cout << b->getName() << std::endl;
}

// interface of object with id
class BaseID {
public:
    virtual void setID(int val) { id = val; }
    virtual int getID() const { return id; }
private:
    int id = 0;
};

// user of interface of objects with id
void idUser(BaseID* b) {
    std::cout << b->getID() << std::endl;
}

class MyObj1 : public BaseID, public BaseName {
public:
    void setName(const std::string& val) override { 
        /* update internal state that depends on name. this is why "virtual" is required */
        BaseName::setName(val);
    }

    /* methods and fields specific to MyObj1 */
};

// MyObj2,...,MyObj999

class MyObj1000 : public BaseID, public BaseName {
public:
    /* methods and fields specific to MyObj1000 */
};

int main() {
    MyObj1 o1;
    o1.setName("xxx");
    o1.setID(18);

    MyObj1000 o2;
    o2.setName("yyy");
    o2.setID(-738);

    nameUser(&o1);
    nameUser(&o2);
    idUser(&o1);
    idUser(&o2);
}

2 个答案:

答案 0 :(得分:1)

仅应在派生类为IS-A基类的情况下继承,也就是说,将其作为继承的类在系统中起作用是有意义的。产生多重继承的地方就是您的派生类存在于多个“域”中,例如,您可能拥有一个bird的{​​{1}}类,那么您将从serializable和{ {1}},并且可以从每个实例继承实现。

但是您拥有1000个派生类的事实对我来说是Code Smell。您也许应该退后一步,对设计和要实现的目标进行更广泛的了解。对此有更多的了解将有助于我们给出更好的答案。

答案 1 :(得分:1)

仅应在派生类为IS-A基类的情况下进行继承,也就是说,将其作为继承的类在系统中起作用是有意义的。

这表示在C ++中多重继承是完全可以的。一个类可以具有多个IS-A基类。所谓的混入类或接口也是一种好习惯。类为类增加了一些功能,而又不会对其余的类产生太大的影响。例如,继承一个Serializable基类。

我想避免的一件事是钻石问题。那就是继承两个或多个类,这些类又从同一个基类继承。例如

class Base { int base; };
class A : public Base { };
class B : public Base { };
class X : public class A, B { };

在这种情况下,很难理解X.base是何时产生的,并且A和B易于使用Base类。这将导致难以调试的错误。