仅用于代码重用c ++的继承

Posted

技术标签:

【中文标题】仅用于代码重用c ++的继承【英文标题】:Inheritance only for code reuse c++ 【发布时间】:2012-09-26 21:04:33 【问题描述】:

我有 A 和 B 类。现在我需要编写一个新的 C 类,它将使用 A 和 B 中的一些字段和方法,但不是全部。 (我将使用 A 和 B 中大约 50% 的东西)。

现在我正在考虑从 A 和 B 继承。但这会使 C 包含很多没有意义的字段和方法。

基本上,我使用继承只是为了代码重用,否则我将不得不从 A 和 B 复制和粘贴多行代码。

这种做法真的很糟糕吗?有不同的方法吗?

谢谢!

【问题讨论】:

感谢大家的回复!我真的很感激他们。我考虑使用继承而不是组合的原因是因为我还需要修改 A 和 B 中的一些行为。另外我真的不想编辑 A 和 B。 【参考方案1】:

继承没有“是a的一半”的概念,因此绝对不是这里要走的路。这听起来像是作曲的主要案例。

听起来AB 有不止一个“功能”,因为它们中的一半足以组成C

我不确定这是否适用于您的情况,但请考虑将 A 分成两部分,A1A2,并在 A1 中使用 C 所需的功能。然后对B 执行相同的操作到B1B2

A 将是A1A2 的组合,B 将是B1B2 的组合,C 将是A1 和@987654339 的组合@。它们都没有任何不必要的功能或数据。

【讨论】:

【参考方案2】:

继承应该使用“is a”范式。 这意味着如果 C 是 A 和 B 的一种,则 C 应该从 A,B 继承。 如果您已经在使用多重继承,您可能希望将 A 和 B 分解为多个类,每个类处理一小段代码,然后仅从您需要的部分继承 C - 这样 C 只会占用它的空间需要(假设 A 和 B 有很多成员)。 请记住,您的类的大小不受成员方法的影响,仅受成员数据的影响。

【讨论】:

在 C++ 中,public 继承意味着“is-a”。其他级别的继承意味着不同的东西......比如“is-implemented-in-terms-of”。 @cHao - 没错,但即使使用私有继承 - C 访问它不需要的部分代码也是一种不好的做法。此外 - 如果 A 和 B 有很多数据成员,C 的大小仍然会比它应该的大。【参考方案3】:

正如 Herb Sutter 和 Andrei Alexandrescu 在他们的书 C++ Coding Standards (item 34) 中解释的那样,继承是 C++ 允许您在两个类之间使用的最紧密的关系之一 - 它仅次于 friend类之间的关系。

根据S.O.L.I.D principles of OO,一个成功的设计应该以类之间的松散耦合为目标——这意味着将依赖关系保持在最低限度;这反过来意味着继承是一个应该小心使用的工具。

当然,依赖项通常需要存在于某处,因此可以从根本没有实现的类继承(类似于 C# 和爪哇)。例如

class IDriveable

public:
    virtual void GoForward() = 0;
    virtual void GoBackward() = 0;
;

class Car : public IDriveable  /* etc. */ ;
class Bus : public IDriveable  /* etc. */ ;
class Train : public IDriveable  /* etc. */ ;

使用这种方法,如果您的代码元素在多个 Drivable 类之间重用,您通常会使用组合或其他一些较弱的关系来消除重复代码。 例如也许您想将代码重用于 TurnLeft 用于 BusCar 但不是 Train 左转不合逻辑,因此 TurnLeft 可能最终会出现在一个单独的类中,该类是 @ 的成员987654331@和Car

此外,任何可能需要了解所有模糊相关类的功能都将在类层次结构之外,只知道接口/基础而不是具体的实现细节。

最终结果可能是少量的额外代码用于组合,但通常设计不太复杂,而且通常更易于管理。设计这样的代码没有任何硬性规则,因为它完全取决于您要解决的独特问题。

还有其他方法可以在不紧密耦合的情况下重用代码 - 模板让您可以隐式定义接口,而无需包含纯 virtual 函数的空类(模板提供额外的类型安全 - 这是非常的事情,但它们在语法上稍微复杂一些);

还有一些方法可以使用std::function 和 lambdas 以便以更实用的方式重用代码 - 同样,在传递函数对象时通常不涉及紧密的依赖关系。

【讨论】:

【参考方案4】:

这很糟糕。只有在逻辑上有意义时才使用继承。

改用组合(private 继承是组合的一种形式,但最好是老派):

class C

    A a;
    B b;

如果C不是由AB组成,也可以保留指针,这样C的大小就不会增加。

【讨论】:

【参考方案5】:

听起来你可以从重构代码中受益:

而不是这个:

class A

  virtual void foo();
  virtual void bar();
;

class B

  virtual void biz();
  virtual void baz();
;

class C : public A, public B

  virtual void foo()  /* my stuff */ 
  virtual void biz()  /* my other stuff */ +

  // but I don't need bar or baz!
;

考虑在 C 中拆分出 A 和 B 的概念上不同的部分:

class core_A

  virtual void foo();
;

class A : public core_A

  virtual void bar();
;

class core_B

  virtual void biz();
;

class B : public core_B

  virtual void baz();
;

class C : public core_A, public core_B

  virtual void foo()  /* my stuff */ 
  virtual void biz()  /* my other stuff */ +

  // Great, I don't have bar or baz!
;

【讨论】:

以上是关于仅用于代码重用c ++的继承的主要内容,如果未能解决你的问题,请参考以下文章

用于消除 Symfony2 中重复代码的特征、辅助服务或继承

JAVA:编写一个动物的继承关系代码.

C,C++,C#区别

复习笔记——C++模板

谁给推荐个c++代码混淆工具

自动旋转代码适用于 iPhone 但不适用于 iPad