我想为可以从外部方法/类访问的类的不同属性定义接口。
我使用了多个实现继承(=来自与接口继承相对的具体类的继承),这被认为是一种不好的做法(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);
}
答案 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类。这将导致难以调试的错误。