【问题标题】:How to resolve inheritance of abstract base class with multiple derived classes如何解决具有多个派生类的抽象基类的继承问题
【发布时间】:2013-06-26 14:55:12
【问题描述】:

我想使用抽象基类来实现接口和可用性。这个抽象基类将用作几个不同派生类的父类,每个派生类都需要抽象基类中纯虚函数的某个子集。我的方案大纲如下。

abstractclass.hpp

AbstractClass{
/* Declaration */
void pureVirt1() = 0;
void pureVirt2() = 0;
void pureVirt3() = 0;
};

派生类1.hpp

DerivedClass1{
/* Declaration */
void pureVirt1();
};

派生类2.hpp

DerivedClass2{
/* Declaration */
void pureVirt2();
};

派生类3.hpp

DerivedClass3{
/* Declaration */
void pureVirt3();
};

鉴于当前的实现,以上所有类都是抽象类,不能从这些类创建对象。过去,我通过每个虚拟函数周围的预处理器指令解决了这个问题。

abstractclass.hpp

AbstractClass{
#ifdef _1
void purVirt1()=0;
#endif
...

在每个派生类的头文件的顶部,在包含到 abstractclass.hpp 之前,我会写如下内容

派生类1.hpp

#define _1
#include"abstractclass.hpp"
...

当我在处理小型项目而不是编写 make 文件时,这很有效。只要我将指令保存在正确的位置,抽象类的标头就会根据使用它的派生类有效地进行更改。现在我正在使用makefile,这不起作用。 abstractcalss.cpp 编译时没有头文件中的任何虚函数,因为它是与派生类分开编译的。我正在为此寻找一个好的解决方法。

我想要这个功能,因为我有许多来自这个抽象基类的类似派生类,这些派生类被我编写的各种其他工具使用。我想让这些其他工具尽可能简单,只使用指向抽象类的指针,而不是为所有内容编写模板类。

--更多信息 我有一种情况,AbstractClass 与 SubAbstractClass 存在关联关系,并且是通过在 AbstractClass 中使用指向 SubAbstractClass 的指针来实现的。此外,对于每个派生类,都存在与 SubDerivedClass1、SubDerivedClass2 类似的 has-a 关系……我不想为我创建的每个新类编写容器,特别是因为我可以组合我的派生类来创建新类重要且实用的新类的任何此类组合都需要创建适当的子类集。为此,有一个 ABC 以允许指针被声明一次并适用于任何派生类是很有用的。

【问题讨论】:

  • 我认为这根本没有任何意义。 #1,定义ABC(抽象基类)的重点是声明所有派生类实现的方法,因此它们是可互换的。 #2,使用预处理器修改 ABC(因此最终会出现多个冲突的定义)会导致未定义的行为。
  • 目前确实如此,这就是为什么我正在寻找更好的解决方案。
  • 如果不同的派生类从具有不同功能的基类派生,那么基类应该是不同的类,而不是迟早会违反单一定义规则的杂种混合。
  • 同意,这有点歪曲了类型系统。在您想要的情况下,成为AbstractClass 没有任何意义——它不会告诉您任何有关您的对象支持的实际功能的信息。 AbstractClass 在你的设计中的目的是什么?
  • @vckngs7:你想在这里解决什么问题?即为什么你想要一个通用的基类?

标签: c++ inheritance makefile abstract-class virtual-functions


【解决方案1】:

[...] 几个不同的派生类,每个派生类都需要一个 抽象库中纯虚函数的某个子集 类。

显然,这行不通。此外,在我看来,你试图让事情变得更简单的尝试会产生相反的效果。通过引入预处理器黑魔法来注释和注释接口的特定部分,您使事情变得更加复杂。

你在这里没有桨就逆流而上。与其只使用一个接口类来零碎地添加和删除方法,不如开发几个不同的接口类,它们可以更好地模块化功能:

AbstractClass1{
/* Declaration */
void pureVirt1() = 0;
};

AbstractClass2{
/* Declaration */
void pureVirt2() = 0;
};

AbstractClass3{
/* Declaration */
void pureVirt3() = 0;
};

试图制作一个通用的、神级的,你吹掉部分以满足特定模块的需求,最终会咬你,而且很难。考虑一下当您需要在同一个翻译单元中对接口进行两个实例化时会发生什么,但每个实例化都有不同的 #defineed 拼凑而成。对我来说听起来像是一场噩梦。

【讨论】:

  • 您的解决方案似乎不是最优的,原因有两个。首先,它不能很好地扩展。如果类的规范发生更改并且我需要更多 PVF,那么您的解决方案不仅需要我编写全新的类,而且还需要更改任何需要访问这些新类的代码。此外,我的实际实现包含超过 3 个 PVF,因此需要更多 ABC 来实现您的解决方案。
  • 编写新代码以利用新功能是当规范发生变化时无论如何你都必须要做的事情,无论你如何解决这个问题。此外,我会说将层次结构分解为单独的 ABC 和 PVF 可以很好地扩展。比#define 方法好得多,因为增加的模块化比单体类更容易维护,并且表现出更松散的耦合和更少的脆弱内部。
  • 是的,你必须写一个新的类,但是你不需要比#defines写更多的东西,而且它有可能变得更简单维护。
  • 不要误以为我的观点是#define 解决方案更好,只是我认为这个解决方案不值得重写。就目前而言,我有 10 个使用这些方法的其他工具和分布在多个类中的 40 多个纯虚函数。这是相当多的重写,当我编写新工具并添加新的 ABC 而不是 PVF 时,我需要编写更多的类并更改更多的工具来使用它们。
  • 我明白你在说什么,我希望你能得到更适合你需求的建议。
【解决方案2】:

从一个通用的基类开始。

class AbstractBaseClass {
  // common stuff goes here
};

然后,为子版本创建抽象接口:

class AbstractSubClass1:public AbstractBaseClass {
public:
  void pureVirt1() = 0;
};
class AbstractSubClass2:public AbstractBaseClass {
public:
  void pureVirt2() = 0;
};
class AbstractSubClass3:public AbstractBaseClass {
public:
  void pureVirt3() = 0;
};

其中包含抽象的pureVirt 方法。

最后,从子类派生你的实现类:

class Derived1 : public AbstractSubClass1 {
  virtual void pureVirt1() override; // override if your compiler supports it
};
class Derived2 : public AbstractSubClass2 {
  virtual void pureVirt2() override; // override if your compiler supports it
};
class Derived3 : public AbstractSubClass3 {
  virtual void pureVirt3() override; // override if your compiler supports it
};

现在,知道您是特定子类的对象可以访问pureVirtX 成员。那些不仅可以访问通用 AbstractBaseClass 接口的人(不管是什么 - 你提到某些代码不需要知道这些特定的 virtual 方法)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 2013-05-22
    • 2014-08-16
    • 1970-01-01
    • 2012-11-15
    相关资源
    最近更新 更多