在某些情况下,您希望使用一个类作为接口的实现,但您不能修改代码或封装这个类(例如,从它派生)。在其他情况下,您可能希望抢占这种情况。// 让我们假设以下示例,我们有以下“密封”类,其中我们无法更改代码:
class my_sealed_class;
class my_lock
{
friend class my_sealed_class;
my_lock() {}
my_lock(my_lock const&) {}
};
class my_sealed_class : virtual my_lock
{
private:
int x;
public:
void set_x(int val) { x = val; }
int get_x() const { return x; }
};
基本上有两种设计模式可供您选择: 1.) 作为接口的成员对象实现。
template<class implementation>
class my_interface0
{
private:
implementation m_impl;
public:
void set_x(int val) { m_impl.set_x(val); }
int get_x() const { return m_impl.get_x(); }
};
由于 C++03 模板的功能,这是非常类型安全的。许多编译器将内联“实现”的实际调用的成员函数 - 不幸的是,不是每个编译器。在某些情况下,我们必须处理不必要的调用。
2.) 使用 CRTP 模式而不从基础派生。
template<class derived>
class my_interface1
{
public:
void set_x(int val) { reinterpret_cast<derived*>(this)->set_x(val); }
int get_x() const { return reinterpret_cast<derived const*>(this)->get_x(); }
};
是的,我知道:这不是使用 CRTP 模式的正常方式。通常我们会这样定义:
template<class derived> class base { void foo() { static_cast<derived*>(this)->foo(); } };
class derived : base<derived> { void foo() {} };
不幸的是,我们不能这样做。请记住,我们不能更改实现类的代码。让我们考虑一下在我的 CRTP 模式版本中使用 my_interface1 到底会发生什么。在编程语言 C++03 的理论中,将派生类指针强制转换为他的基类之一是安全的。另一方面,在相反的方向(从基础到派生)或完全不同的类型进行转换是不安全的。不能保证内存对象已为基类和派生类保留了所需的字节数。但是,实际上,这不属于这个示例,因为我们的接口不包含任何成员。因此,它的大小是 0 字节(请注意 operator new 确实分配了至少 1 字节,即使类为空时也是如此)。在这种情况下,它' s - 实际上 - 将任何类型指针转换为 'my_interface1 指针是安全的。决定性的优势是,几乎每个编译器都会内联对实际调用的成员函数的调用。
int main()
{
// Not even worth to mention: That's safe.
my_sealed_class msc;
msc.set_x(1);
// Safe, because 'my_interface0' instantiates 'my_sealed_class'.
my_interface0<my_sealed_class> mi0;
mi0.set_x(2);
// Absolutely unsafe, because 'my_interface1' will access memory which wasn't allocated by it.
my_interface1<my_sealed_class> mi1;
mi1.set_x(3);
// Safe, because 'my_interface1*' references an my_sealed_class object.
my_interface1<my_sealed_class>* pmi1 = reinterpret_cast<my_interface1<my_sealed_class>*>(&msc);
pmi1->set_x(4);
return 0;
}
那么,您认为最佳实践是什么?此致。