c++ - C++03 后期绑定(bind)接口(interface)的设计模式

标签 c++ design-patterns interface crtp

有些情况下你想使用类作为接口(interface)的实现,但你不能 修改代码或封装此类(例如从中派生)。在其他情况下,您可能想要 先发制人这样的情况。 // 让我们假设下面的例子,我们有下面的“密封”类,我们不能 更改代码:

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.) 作为接口(interface)的成员对象实现。

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 模式而不派生自 base。

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() {} };

很遗憾,我们无法做到这一点。请记住,我们不能更改实现类的代码。 让我们考虑一下将 my_interface1 与我的 CRTP 模式版本一起使用到底会发生什么。在 编程语言 C++03 的理论,将派生类指针转换为他的一个是安全的 基类。另一方面,朝相反的方向施法(从基地到 派生)或完全不同的类型。不能保证内存对象已经保留 基类和派生类所需的字节数。 但是,实际上,这不属于这个例子,因为我们的接口(interface)不包含任何 成员。因此,它的大小是 0 字节(请注意,operator new 确实分配了至少 1 个字节,即使 当类(class)为空时)。在这种情况下,将任何类型指针转换为 - 实际上 - 是安全的 '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;
}

那么,您认为最佳做法是什么? 最好的问候。

最佳答案

在我看来选项二都有未定义的行为并且违反了严格的别名规则,这很可能会禁用某些优化或破坏您的代码。此外,任何进入并阅读代码的人几乎肯定会对它的工作原理感到非常困惑。

第一个示例是有效代码,这是大多数程序员都能理解的标准模式,几乎所有编译器都应该内联。我肯定会推荐这种方法。即使在一些编译器无法内联传递调用的奇怪世界中,只有当分析显示额外调用是一个重大瓶颈时,我才会考虑其他一些方法来提高性能。

编辑:回答原始问题的最后一个问题:使用选项 2 而不是选项 1 将永远不是最佳实践。如果选项 1 存在性能问题,可能有更好的方法解决它们而不是未定义的行为。

关于c++ - C++03 后期绑定(bind)接口(interface)的设计模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5706760/

相关文章:

c++ - 计算 fib(n) 被调用 FOR EACH n 的次数

c++ - 在 c++ 中,为什么编译器在 const 也可以工作时选择非常量函数?

c++ - 在 Vista 上保持 cmd.exe 打开

java - 如果单例类构造函数是私有(private)的并且从其他类调用方法*参见代码*那么它是如何工作的

javascript - OO Javascript - 事件

c++ - 为什么不支持容器适配器中元素的初始化和迭代

java - 有一个 ObjectFactory 接口(interface)和实现它的类是好的设计吗

c# - 我应该为消息接收者创建接口(interface)吗?

c# - 类型 T 必须是引用类型,以便在使用接口(interface)时将其用作参数

java - 如果具有相同接口(interface)的类具有相似但不同的方法签名怎么办?