对于动态(运行时)多态性,我建议使用非虚拟接口(NVI)惯用语。该模式使接口保持非虚拟和公共的,析构函数是虚拟和公共的,实现是纯虚拟和私有的。
class DynamicInterface
{
public:
void fun() { do_fun(); }
virtual ~DynamicInterface() = default;
private:
virtual void do_fun() = 0;
};
class DynamicImplementation
:
public DynamicInterface
{
private:
virtual void do_fun() { }
};
动态多态的好处在于,在运行时,您可以将任何派生类传递到期望接口基类的指针或引用中。运行时系统会自动将“this”指针从其静态基类型向下转换为其动态派生类型,并调用相应的实现(通常通过具有指向虚函数的表来实现)。
对于静态(编译时)多态性,我建议使用“奇异递归模板模式”(Curiously Recurring Template Pattern,CRTP)。这要复杂得多,因为动态多态性的自动向下转换必须使用“static_cast”进行。这种静态转换可以在每个静态接口都继承的辅助类中定义。
template<typename Derived>
class enable_down_cast
{
private:
typedef enable_down_cast Base;
public:
Derived const* self() const
{
return static_cast<Derived const*>(this);
}
Derived* self()
{
return static_cast<Derived*>(this);
}
protected:
~enable_down_cast() = default;
};
那么你可以这样定义一个静态接口:
template<typename Impl>
class StaticInterface
:
public enable_down_cast< Impl >
{
private:
using enable_down_cast< Impl >::self;
public:
void fun() { self()->do_fun(); }
protected:
~StaticInterface() = default;
};
最后,您可以创建一个从该接口继承的实现,并将其本身作为参数。
class StaticImplementation
:
public StaticInterface< StaticImplementation >
{
private:
friend class StaticInterface< StaticImplementation > ;
void do_fun() { }
};
这仍然允许您拥有同一接口的多个实现,但您需要在编译时知道您正在调用哪个实现。
那么何时使用哪种形式? 两种形式都可以让您重用常见接口并在接口类中注入前/后条件测试。动态多态性的优势在于您具有运行时灵活性,但您需要通过虚函数调用来付出代价(通常是通过函数指针调用,很少有机会进行内联)。静态多态性是其镜像:没有虚函数调用开销,但缺点是您需要更多的样板代码,并且您需要在编译时知道要调用哪个实现。基本上是效率/灵活性的权衡。
注意:对于编译时多态性,您还可以使用模板参数。通过CRTP惯用语法的静态接口和普通模板参数之间的区别在于,CRTP类型接口是显式的(基于成员函数),而模板接口是隐式的(基于有效表达式)。
=delete
呢?它会被继承吗?我可以在它被删除后实现一个方法吗? - emesx