2

我有一个类Foo,我不直接实现它,而是包装外部库(例如FooXternal1FooXternal2)我见过的一种方法是使用预处理器指令作为

#include "config.h"
#include "foo.h"
#ifdef _FOOXTERNAL1_WRAPPER_
//implementation of class Foo using FooXternal1
#endif

#ifdef _FOOXTERNAL2_WRAPPER_
//implementation of class Foo using FooXternal2
#endif

和 aconfig.h用于定义这些预处理器标志(_FOOXTERNAL1_WRAPPER__FOOEXTERNAL2_WRAPPER_)。我的印象是 C++ 程序员社区不赞成这种做法,因为它使用预处理器指令,难以调试等。此外,它不允许两种实现并行存在。

我考虑过创建Foo一个基类并从它继承,以允许两个实现彼此并行存在。但是我遇到了两个问题:

  1. 纯虚函数:cannot instatiate an object of type 'Foo',我在使用过程中需要的。
  2. 虚函数冒着运行没有(正确)实现的对象的风险。

我错过了什么吗?有没有更清洁的方法来做到这一点?

编辑:总而言之,有 3(.5?!) 种包装方法 - 2(.5) 由 icepack 给出,最后一种由 Sergey 给出 1- 使用工厂方法 2- 使用预处理器指令 2.5- 使用 makefile 或IDE 有效地完成预处理器指令的工作 3.5- 使用 Sergay 建议的模板

我正在开发一个资源有限的嵌入式系统,我决定使用template<enum = default_library>模板专业化。便于后期用户理解;至少我是这么认为的

4

4 回答 4

2

如果外部实现的所有方法名称都相似,则可以使用模板。让外部实现看起来像:

class FooX1
{
public:
    void Met1()
    {
        std::cout << "X1\n";
    }
};

class FooX2
{
public:
    void Met1()
    {
        std::cout << "X2\n";
    }
};

然后,您可以使用多种变体。

变体 1。您可以声明模板类型的成员并将所有调用包装到外部实现,即使在调用之前有一些准备。不要忘记impl~Foo析构函数中删除。

template<typename FooX>
class FooVariant1
{
public:
    FooVariant1()
    {
        impl=new FooX();
    }

    void Met1Wrapper()
    {
        impl->Met1();
    }
private:

    FooX *impl;
};

用法:

FooVariant1<FooX1> bar;
bar.Met1Wrapper();

变体 2。您可以从模板参数继承。在这种情况下,您无需声明任何成员,而只需按名称调用实现的方法。

template<typename FooX>
class FooVariant2 : public FooX
{
};

用法:

FooVariant2<FooX1> bar;
bar.Met1();

使用模板的一个缺点是没有简单的方法可以在运行时更改实现。但作为回报,您会得到更优化的代码,因为类型是在编译时生成的,并且没有虚函数表,这会使程序变慢。

于 2012-12-11T15:16:05.870 回答
2

如果您希望这 2 个实现在运行时共存,那么接口是可行的方法(例如,您可以使用工厂方法设计模式来实例化具体对象,就像 @nm 建议的那样)。

如果你可以在编译时决定你需要什么实现,你有几个选择:

  • 仍然使用接口。如果将来您在运行时需要这两种实现,这将允许轻松转换。

  • 使用预处理器指令。就 C++ 而言,这里没有错。这是一个纯粹的设计问题。

  • 将实现放在不同的文件中并配置您的编译器以根据设置编译其中一个 - 这实际上类似于使用预处理器指令,但它更清洁并且不会向您的代码添加垃圾(因为标志位于解决方案/makefile /无论你的编译器使用什么)。

于 2012-12-11T15:16:09.630 回答
1

我唯一不赞成的是将两个实现都包含在同一个源文件中。这可能会让人感到困惑。否则,这是预处理器标志擅长的事情之一,特别是如果您没有同时链接两个库。这就像支持多个操作系统一样。在所有情况下提供一致的接口,并在其他地方隐藏实现细节。

type 是否Foo需要保存特定于每个库的任何信息?如果没有,你也许可以摆脱这个:

#include "Foo.h"

#if defined _FOOXTERNAL1_WRAPPER_
    #include "Foo_impl1.cpp"
#elif defined _FOOXTERNAL2_WRAPPER_
    #include "Foo_impl2.cpp"
#else
    #error "Warn about a missing define here"
#endif

这样您就不必为虚函数或继承而烦恼,并且您仍然可以防止任何成员函数未实现。

于 2012-12-11T15:03:27.107 回答
1

保持Foo抽象。提供工厂方法

Foo* MakeFoo();

分配一个 或 类型的新对象FooImpl1FooImpl2并返回其地址。

关于工厂方法模式的维基百科

于 2012-12-11T15:10:49.783 回答