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

Should I avoid multiple implementation inheritance?

本文关键字:实现 继承 我应该      更新时间:2023-10-16

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

  • 我使用多实现继承(=从具体类继承,而不是从接口继承(,这被认为是一种糟糕的做法(这里的第3点(。

  • 我使用继承来实现代码重用。正如这里所说,为了代码重用,最好使用组合而不是继承。但是对于composition,我所有的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);
}

只有在派生类IS-A基类的情况下才应该继承,也就是说,它在系统中充当它继承的类是有意义的。多重继承是指派生类存在于多个"域"中,例如,您可能有一个bird类也是serializable,您将从animalserializable继承,并且可以从每个继承实现。

但事实上,你有1000个派生类,这对我来说是一种代码气味。你也许应该退后一步,更广泛地审视你的设计和你试图实现的目标。更多地了解这一点将有助于我们给出更好的答案。

只有在派生类IS-A基类的情况下才应该继承,也就是说,它在系统中充当它继承的类是有意义的。

也就是说,多重继承在C++中是非常好的。一个类可以有多个IS-A基类。对于所谓的类或接口中的混合也是一种很好的实践。为类添加了一些功能而不会给类的其他部分带来太多麻烦的类。例如,继承可序列化基类。

不过,我尽量避免的一件事是钻石问题。也就是说,继承两个或多个类,这些类又从同一基类继承。例如

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

在这种情况下,很难理解X.base来自什么地方,A和B很容易对基类进行不同的使用。这将导致难以调试的错误。