【问题标题】:C++: decoupling of interface / implementation w/o using virtual functions?C++:使用虚函数解耦接口/实现?
【发布时间】:2009-12-23 15:57:47
【问题描述】:

在过去的几个月里,我被 Java 宠坏了!我有一个 C++ 项目,我想将类接口(.h 文件)与其实现细节分离。但是类的成员字段必须在其声明中,如果我想调整类的成员字段,似乎我有这种不可避免的依赖链接。

我知道一种方法是使用多态性 + 类继承(使接口成为基类,使实现成为派生类),但如果我没记错的话,这需要虚函数,这是我想要的避免——这是在 DSP 上,最好不要太“C++-y”。

有什么建议吗?

【问题讨论】:

  • 我会说更多的是“被误导”而不是“被宠坏”:) - 在 Java 中,除非标记为“最终”,否则一切都是虚拟的。
  • 嗯,是的,但是 Java 充满了对小型微控制器来说不实用的东西:虚拟方法、同步、垃圾收集、大内存使用等,所以当我使用Java 在我的电脑上。
  • 学习 Java 实际上提高了我的 C++ 设计和编码技能。

标签: c++ interface


【解决方案1】:

你想要PIMPL idiom

【讨论】:

  • 这个名字还是让我有点不寒而栗,但我还是要发同样的东西。 +1
  • 不会通过 pimpl/bridge 等同于通过 vtable 进行额外的取消引用吗?
  • @mmyers 我在发布答案之前查看了那个维基百科链接,并认为它相当糟糕。如果那里有任何 C++ 维基百科,看起来关于 PIMPL 的好文章已经过期了。
  • @Nikolai 如果你想要间接(这是 PIMPL 和虚函数的根源),我看不出有任何不付出代价的方式。
  • @Nikolai N Fetissov 这是一个额外的数据取消引用,而不是函数指针,因此在大多数系统上得到了更好的优化。
【解决方案2】:

你知道,我考虑了这一点以及你对 PIMPL 的反对意见。

我有一个丑陋的技巧,有时我会在这种情况下使用,我讨厌支付间接惩罚。虽然通常我的抱怨是调用 new,而不是指针取消引用。因此,我提出了我丑陋的黑客:

//  IHaveOpaqueData.h

class IHaveOpaqueData {
 public:
    // To make sure there are no alignment problems, maybe ::std::uin64_t
    typedef maximally_aligned_type_t internal_data_t[32];  // Some size I hope is big enough

    void iCanHazMemberFunction();
    // ...
 private:
    internal_data_t data;
};

//  IHaveOpaqueData.cpp
#include <boost/static_assert.hpp>

namespace { // Hide it in an anonymous namespace
struct RealData {
    int icanhazmembervariable_;
    double icanhazdoublevariable_;
};
BOOST_STATIC_ASSERT(sizeof(RealData) < sizeof(IHaveOpaqueData::internal_data_t);
}

void IHaveOpaqueData::iCanHazMemberFunction()
{
    // Use a reference to help the optimize make the right decision
    RealData &datathis = *(reinterpret_cast<RealData *>(&(this->data)));
    datathis.icanhazmembervariable_ = datathis.icanhazdoublevariable_;
}

是的,这很难看。 BOOST_STATIC_ASSERT(或者如果你有一个 C++[01]x 编译器,static_assert 关键字)有助于使它不是一场彻底的灾难。可能有一种聪明的方法可以使用联合来减轻我在对齐问题上的一些抽搐。

【讨论】:

  • 丑是轻描淡写。如果对象是长寿命的,那么为内部数据结构支付新的价格比这更好。如果它是短暂的,那么可能是此对象类型的优化池分配器。或者只是忍受一些额外的编译时间,我宁愿为此付费,也不愿引入这样的代码或主要的运行时影响!
  • 完整的讨论见gotw.ca/gotw/028.htm“The Fast Pimpl Idiom”。尽管这是一件可怕的事情,但我已经编写了 Omnifarious 所做的模板版本,以使其更加安全和可用。不过还是很吓人。
  • 这比快速 pimpl 具有线程安全优势。使快速 pimpl 线程安全会大大降低其速度。当内部数据结构的内容不是 POD 时,我的 hack 变得非常棘手且极易出错。
【解决方案3】:

使用 pimpl 成语。在这里阅读:http://www.devx.com/cplus/Article/28105/0/page/3

这将有助于将实现与接口分离,并减少(至最低限度)所有编译依赖项。您甚至可以避免使用虚函数。

【讨论】:

    【解决方案4】:

    这是一个老想法 :) - 不透明数据类型加上一组函数,即“再次回到 [to C]”:

    
    // oi.hpp
    namespace oi // old idea
    {
        struct opaque; // forward declaration
    
        void init( opaque& ); // ctor
        void fini( opaque& ); // dtor
    
        int get_foo( const opaque& ); // getter
        void set_foo( opaque&, int ); // setter
    }
    // oi.cpp
    namespace oi
    {
        struct opaque // definition
        {
            int foo_; // data members
            // ...
        };
    
        // function definitions
    }
    

    通过引用访问结构的运行时成本可能与使用 pimpl 相同,因此鉴于无法使用 RAII 等一些重要的惯用语,这可能是一个较差的解决方案。

    【讨论】:

      猜你喜欢
      • 2010-11-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-30
      • 2010-12-13
      • 1970-01-01
      • 2019-02-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多