C++;一个简单场景的OO继承正确性 我试图用C++的简单模型创建一个继承层次:课程、模块和课程,其中课程由零或更多的模块组成,模块由零或更多的课程组成。 每个类别还包含其他信息: class course { private: const std:string & name_; const std::duration duration_; const difficulty difficulty_; ... }; class module { private: const std:string & name; const std::duration duration; ... }; class lesson { private: const std:string & name; const std::duration duration; ... };

C++;一个简单场景的OO继承正确性 我试图用C++的简单模型创建一个继承层次:课程、模块和课程,其中课程由零或更多的模块组成,模块由零或更多的课程组成。 每个类别还包含其他信息: class course { private: const std:string & name_; const std::duration duration_; const difficulty difficulty_; ... }; class module { private: const std:string & name; const std::duration duration; ... }; class lesson { private: const std:string & name; const std::duration duration; ... };,c++,oop,inheritance,C++,Oop,Inheritance,我的问题是: 有这样一个类是正确的吗?这三个类都是从这个类继承的,它包含它们的公共属性 就我个人而言,我认为以以下方式从Vector继承会更为正确: class course : public std::vector<module> { private: const std:string & name_; const std::duration duration_; const difficulty difficulty_; ... };

我的问题是:

  • 有这样一个类是正确的吗?这三个类都是从这个类继承的,它包含它们的公共属性
就我个人而言,我认为以以下方式从Vector继承会更为正确:

class course : public std::vector<module>
{
private:
    const std:string & name_;
    const std::duration duration_;
    const difficulty difficulty_;
    ...
};

class module : public std::vector<lesson>
{
private:
    const std:string & name;
    const std::duration duration;
    ...
};

class lesson
{
private:
    const std:string & name;
    const std::duration duration;
    ...
};
课程:公共std::vector
{
私人:
常量标准:字符串和名称;
const std::持续时间;
常量难度;
...
};
类模块:公共std::vector
{
私人:
常量标准:字符串和名称;
const std::持续时间;
...
};
课堂
{
私人:
常量标准:字符串和名称;
const std::持续时间;
...
};
由于课程是模块的集合,而模块是具有一些附加属性的课程集合,因此从vector继承将提供添加、删除等模块和课程所需的所有标准功能

  • 为什么不能两者兼而有之?我在学校学习继承的方式,使用Java,我被教导多重继承是邪恶的,为了正确的OO继承而远离多重继承,使用等等。有很多人不相信多重继承,还有很多人对多重继承深信不疑
另一种可能的方式是:

class course : public namedentity
{
private:
    std::vector<module> modules_;
    ...
    const difficulty difficulty_;
    ...
};

class module : public namedentity
{
private:
    std::vector<lesson> lesons_;
    ...
};

class lesson : public namedentity
{
private:
    ...
};
课程类别:公共名称身份
{
私人:
向量模;
...
常量难度;
...
};
类模块:公共名称身份
{
私人:
std::向量lesons;
...
};
课程:公共名称身份
{
私人:
...
};
其中namedentity包含所有公共属性,这些私有向量包含它们的子属性


我想归根结底,课程真的是
vector
,模块真的是
vector
?这是否违反LSP或其他OO原则?将孩子作为财产会更好吗?

不建议公开继承向量,因为课程、课程或模块不是向量

碰巧您使用向量来实现它们,因为一个模块可以有多个课程。如果tommorow使用集合、映射、链表或数据库映射器来实现对象,那么如果基于向量继承,就必须重写整个代码。别忘了,标准向量emplace(),push_back(),erase()。。。将需要过载,因为模块中课程的所有这些操作都需要调整课程持续时间

顺便说一句,类设计的一个很好的经验法则是为“is-a”关系使用继承,为“has-a”使用组合

更多支持和反对向量继承的论点如下

第二种选择看起来更好,因为课程、课程、模块实际上是一个命名实体。通过这种方法,对象之间的关系也更加清晰

这两种方法都符合利斯科夫替代原则:

  • 如果为
    namedentity
    编写代码,则可以使用相同的代码将命名实体替换为这些子类中的任何一个(前提是它们都实现了可以与基类使用的参数相同的构造函数)。无论您在未来对数据结构进行何种改进,这都是有意义的
  • 如果您编写代码来处理
    向量
    ,则相同的代码可以用于
    课程
    。但是,您可能会在构造函数中使用不同的语义。如果更改实现,LSP将适得其反
一般来说,从标准库容器继承是个坏主意。一个引人注目的原因是,在您描述的用例中,如果不要求对容器上执行的操作进行一些约束,就很难不提供派生类提供的额外功能,这会破坏可替换性

相反,如果您认为类的API应该公开子类集合上的容器操作,那么提供一个方法来引用存储为类成员的容器就更简单、更容易了。更好的做法是只公开您所需的封闭容器的功能,这样您就有了一个地方来强制实现您的类现在或不久的将来需要的任何不变量

关于继承公共属性:通常这是一个好主意,但这样做有两个动机,即代码重用和抽象,它们不一定总是一致的

从纯抽象类继承或多或少等同于在Java中实现接口,除了性能方面的考虑之外,只要稍微注意一下,就没有理由避免从此类类进行多次继承。另一方面,使用继承作为代码重用的机制允许您在不使用所有代码的情况下使用本质上是一种组合

更具体地说:

  • 我建议不要继承自
    std::vector
    。提供在包含的
    向量
    上公开所需操作的方法。这使您既可以选择更改容器实现,也可以使用可能需要的任何类不变量

  • 如果代码的任何其他部分只关心这些对象的名称,那么从
    namedentity
    继承是一个好的设计

  • 如果所有这些类都需要非纯抽象的
    namedentity
    的功能,那么这是一个不同但也是有效的理由