我有一个类Foo
,我没有直接实现,但包装外部库(例如FooXternal1
或FooXternal2
)
我见过这样做的一种方法是使用预处理程序指令作为
#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
和config.h
用于定义这些预处理程序标志(_FOOXTERNAL1_WRAPPER_
和_FOOEXTERNAL2_WRAPPER_
)。
我的印象是C ++程序员社区不赞成,因为它使用预处理程序指令,很难调试等等。此外,它不允许两个实现的并行存在。
我考虑过将Foo
作为基类并从中继承,以允许两个实现彼此并行存在。但我遇到了两个问题:
cannot instatiate an object of type 'Foo'
,我在使用时需要它。我错过了什么吗?有更清洁的方法吗?
编辑:总结一下,有3种(.5?!)方式进行包装 - 2(.5)由icepack提供,最后由Sergey提供 1-使用工厂方法 2-使用预处理程序指令 2.5-使用makefile或IDE有效地完成预处理程序指令的工作 3.5-使用Sergay建议的模板
我正在开发一个资源有限的嵌入式系统,我决定使用template<enum = default_library>
和模板专业化。对于以后的用户来说很容易理解;至少那就是我的想法
答案 0 :(得分: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();
使用模板的一个缺点是没有简单的方法可以在运行时更改实现。但作为回报,您可以获得更优化的代码,因为类型是在编译时生成的,并且没有虚函数表,这可能会使程序变慢。
答案 1 :(得分:2)
如果您希望2个实现在运行时共存,那么接口是可行的方法(例如,您可以使用工厂方法设计模式来实例化具体对象,如@n.m。建议的那样)。
如果你可以在编译时决定你需要什么样的实现,你有几个选择:
仍在使用界面。如果将来在运行时需要两个实现,这将允许轻松转换。
使用预处理程序指令。就C ++而言,这里没有任何错误。这是一个纯粹的设计问题。
将实现放在不同的文件中,并将编译器配置为根据设置编译其中任何一个 - 这实际上类似于使用预处理器指令但它更干净并且不会给代码添加垃圾(因为标志在解决方案/ makefile /编译器使用的任何内容中。
答案 2 :(得分:1)
我唯一不赞成的是将两种实现都包含在同一个源文件中。这可能会让人感到困惑。否则,这是预处理程序标志擅长的事情之一,特别是如果您没有同时链接两个库。这就像支持多个操作系统一样。在所有情况下提供一致的界面,并在其他地方隐藏实施细节。
类型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
通过这种方式,您不必费心使用虚函数或继承,并且仍然可以防止任何成员函数未实现。
答案 3 :(得分:1)
保持Foo
摘要。提供工厂方法
Foo* MakeFoo();
分配FooImpl1
或FooImpl2
类型的新对象,并返回其地址。