假设我有以下遗留代码,我有机会改进:
class BaseInterfaceClass
{
#if( HAS_FEATURE_A == 1 )
void doFeatureA1() = 0;
void doFeatureA2() = 0;
#endif
#if( HAS_FEATURE_B == 1 )
void doFeatureB1() = 0;
void doFeatureB2() = 0;
void doFeatureB3() = 0;
#endif
#if( HAS_FEATURE_C == 1 )
void doFeatureC1() = 0;
#endif
void doBaseFeature1() = 0;
void doBaseFeature2() = 0;
};
有没有办法取消在编译时定义接口的#ifdef
方法,而不会增加新设计的运行时大小?换句话说,我需要保留这样一个事实,即仅使用HAS_FEATURE_A
编译的派生类将不包含HAS_FEATURE_B
或HAS_FEATURE_C
的代码(与链接的代码相比,不是实际上是因为运行时检查而使用的。)
一些注释
功能标志不是互斥的。可以定义它们的任何组合。
正如我所知,为每个特征定义一个子类,然后使用多重继承将所需的接口组合在一起是不够的。请记住,派生类必须能够在编译时实现任何功能组合,并且不包含任何未为该编译定义的功能。
更改代码的政治成本意味着我必须能够摆脱所有指令,或者根本不做。换句话说,仅仅移动#ifdef
定义以使基类更漂亮是不够的。
有足够的功能组合,任何蛮力方法都是不现实的。我不打算制作成千上万的子类。
我不知道模板,但我愿意知道这是否是关键。
答案 0 :(得分:2)
有办法。但它们真的有用吗?
许多图书馆使用#if
条件编译来提出其他选择加入功能。这是一个长期的惯例。
您可以使用混合来“消除”这一点,但不确定代码会因此而变得更具可读性。相反,使用您的代码的人现在必须了解之前发生的事情。
另一方面,由于我们谈论的是接口,您可能很容易将分割接口分成几个组件(如果可能),每个组件都专用于许多功能。知道你是否能够做到这一点需要更多关于班级错综复杂的知识......但它对我来说太像神的对象了。
答案 1 :(得分:1)
在这种情况下,我肯定会避免使用预处理器。
您可以将您的功能分成不同的接口,并让您的派生类选择并确切选择它将实现哪些功能(以及哪些接口)。
然后,您可能需要使用另一个查找界面来获取功能:
class IFeature
{
};
class IFeatureA : public IFeature
{
virtual void DoSomething() = 0;
};
class IFeatureB : public IFeature
{
virtual void DoSomethingElse() = 0;
};
class IFullComponent
{
virtual void GetFeature( GUID featureId, IFeature** ppFeature ) = 0;
};
在这种情况下,您的要素界面必须从常见的东西中派生出来。
是的,此解决方案将使用多重继承(即您的具体组件将派生自IFeatureXX接口以及IFullComponent),但即使对于因感知到的额外复杂性而避免多重继承的人,请记住您只是继承接口,而不是实际实现。即使是在语言级别不支持多继承的java和C#也允许你这样做。
答案 2 :(得分:1)
假设您需要保留BaseInterfaceClass
而不是使用它来更改类的继承(否则只需为每个功能定义一个接口并仅继承您需要的接口)。
您可以将多重继承与专用模板结合使用,以仅获取所需的接口部分:
template<bool B> class FeatureABase {};
template<> class FeatureABase<true> {
public:
virtual void doFeatureA1() = 0;
virtual void doFeatureA2() = 0;
};
//similar definitions for FeatureBBase and FutureCBase here
class BaseInterfaceClass: public FeatureABase<HAS_FEATURE_A>, public FeatureBBase<HAS_FEATURE_B>, public FeatureCBase<HAS_FEATURE_C>
{/*not flag dependent parts here*/};
//If you want to get rid of the preprocessor flags completely, you could define
//BaseInterfaceClass as a template itself:
template<bool HasA, bool HasB, bool HasC>
class BaseInterfaceClass: public FeatureABase<HasA>, public FeatureBBase<HasB>, public FeatureCBase<HasC>
{/*not flag dependent parts here*/};
如果您不想使用多重继承(因为您不想让对象变大),可以将其展开到一个继承链中:
template<bool B> class FeatureBBase: public FeatureABase<HAS_FEATURE_A> {};
template<> class FeatureBBase<true>: public FeatureABase<HAS_FEATURE_A> {
public:
virtual void doFeatureB1() = 0;
virtual void doFeatureB2() = 0;
virtual void doFeatureB3() = 0;
};
template<bool B> class FeatureCBase: public FeatureBBase<HAS_FEATURE_B> {};
template<> class FeatureCBase<true>: public FeatureBBase<HAS_FEATURE_B> {
public:
virtual void doFeatureC1() = 0;
}
class BaseInterfaceClass: public FeatureCBase<HAS_FEATURE_C>
{/*not flag dependent parts here*/};
这假设您的featureflags基本上是bool,因此如果未启用该功能,HAS_FEATURE_A
为0
。如果不是这种情况,您可以将模板参数设为int
,并专门针对1
而不是true
。如果未使用的功能标志未定义,则无法完全摆脱使用#if
或#ifdef
,因为这是根据是否定义宏来做出决策的唯一方法。