繁体   English   中英

使用PIMPL习语时有没有办法限制重复的样板?

[英]Is there any way to limit repetitive boilerplate when using the PIMPL idiom?

我有以下内容:

// foo.h:
class foo {
public:
    foo(); 
    ~foo();
    // note: the param type repetition here is only incidental, assume the
    // functions can't easily be made to share type signatures
    void bar(a b, c d);               
    void baz(a b, c d, e f, g h, i j);
    void quux(a b, c d, e f, g h, i j);
private:
    class impl;
    impl *m_pimpl;
}

然后:

// foo.cpp:
class foo::impl {
public:
    void bar(a b, c d);
    void baz(a b, c d, e f, g h, i j);
    void quux(a b, c d, e f, g h, i j);

private:
    // lots of private state, helper functions, etc. 
};

void foo::impl::bar(a b, c d) { ... }
void foo::impl::baz(a b, c d, e f, g h, i j) { ... }
void foo::impl::quux(a b, c d, e f, g h, i j) { ... }

foo::foo() : m_pimpl(new impl()) { }
foo::~foo() { delete m_pimpl; m_pimpl = NULL; }
void foo::bar(a b, c d) {
    return m_pimpl->bar(b, d);
}
void foo::baz(a b, c d, e f, g h, i j) {
    return m_pimpl->baz(b, d, f, h, j)
}
void foo::quux(a b, c d, e f, g h, i j) {
    return m_pimpl->quux(b, d, f, h, j);
}

这里有很多重复的barbazquux

  • 曾经在foo的声明中
  • 一旦进入foo::impl的声明
  • 一旦进入foo::impl的定义
  • foo的定义中有两次:一次用于foo的函数参数列表,再次调用相应的foo::impl函数。

对于除了最后一个之外的每一个,我必须写出整个参数列表,其类型可以更多地涉及。

如果有的话,减少重复的最佳方法是什么? 一个简单的方法是内联foo::impl公共函数的定义,但是除了设计类以外的任何东西以外还有更少的公共函数吗?

使用签名,我们可以将其减少到3个提及,加上1组前锋:

using signature_1 = int(int);

struct foo {
  signature_1 x;
  foo();
  ~foo();
private:
  struct fooimpl;
  std::unique_ptr<fooimpl> pimpl;
};

int main() {
  foo f;
  std::cout << f.x(1) << '\n';
}
struct foo::fooimpl {
  signature_1 x;
  int v = 3;
};

foo::~foo() = default;
foo::foo():pimpl(new foo::fooimpl()){}

int foo::x(int y){return pimpl->x(y);}
int foo::fooimpl::x(int y){return y+v;}

如果我们的pimpl是纯虚拟类,我们可以将它降低到2 +前进。 decltype(&foo::method) - > signature写一个映射,并让纯虚拟接口使用该(和decltype)来创建纯虚拟类的签名。

foo写入一次签名,decltype-在foo_impl_interface确定它,然后在cpp文件中的foo_impl内实现它。 再加上一组前锋。

 template<class MethodPtrType>
 struct method_sig;
 template<class MethodPtrType>
 using method_sig_t = typename method_sig<MethodPtrType>::type;
 template<class T, class R, class...Args>
 struct method_sig< R(T::*)(Args...) > {
   using type=R(Args...);
 };

加上另外11个奇数专长(叹气)来获得所有案例。 const& const const&& const volatile const volatile& const volatile&& & && 据我所知, volatile volatile& volatile&& qualifiers都是必需的)。

现在method_sig_t<decltype(&foo::x)>int(int) ,我们可以使用它:

struct foo_impl_interface {
  virtual method_sig_t<decltype(&foo::x)> x = 0;
  virtual ~foo_impl_interface() {}
};

假设我们使用pimpl来规范我们的类型而不是隐藏状态。


最后,不是在pimpl中实现大部分代码,而只是将STATE存储在pimpl中,而是将代码保留在类本身中。

这给你“其他人不依赖于我的大小”:谁关心代码是在foo还是foo_impl中读取foo_impl的状态? 所以如果你没有做foo_impl_interface技术,为什么要前进呢?

如果您的公共类只是代理调用实现,请考虑使用interface - > implementation model而不是pimpl。

您只能通过用户代码中的接口来引用您的实现,其中class foo是接口

class foo {
public:
    virtual void bar(a b, c d) = 0;               
    virtual void baz(a b, c d, e f, g h, i j) = 0;
    virtual void quux(a b, c d, e f, g h, i j) = 0;
    virtual ~foo(){}
}

pimpl背后的想法是将私有数据和函数存储在单独的对象指针中,这样您就不会在数据存储和处理更改时破坏公共类接口。 但它并不意味着所有代码都移动到私有对象。 因此,您通常会在适当的位置实施公共功能。 当公共函数实现发生更改时,您不会破坏用户代码,因为您的公共函数实现将与私有类定义一起隐藏。

您可以这样做的一种方法是定义一个接口类,这样您就不需要重新声明所有内容并使用传递指针语义(重载operator->)

这是一个例子: 是否可以在c ++中编写敏捷的Pimpl?

要结束这个问题:最终我认为Adrian的评论最好地解决了这个问题:“我倾向于在类定义中实现impl类的方法,这减少了一些重复。”

我上面的代码将成为:

// foo.cpp:
class foo::impl {
public:
    // lots of private state, helper functions, etc. 
};

foo::foo() : m_pimpl(new impl()) { }
foo::~foo() { delete m_pimpl; m_pimpl = NULL; }
void foo::bar(a b, c d) {
    ... code using m_pimpl-> when necessary ...
}
void foo::baz(a b, c d, e f, g h, i j) {
    ... code using m_pimpl-> when necessary ...
}
void foo::quux(a b, c d, e f, g h, i j) {
    ... code using m_pimpl-> when necessary ...
}

现在它更合理 - 只有一个用于声明,一个用于定义。 在转换类以使用m_pimpl->添加m_pimpl-> s时会有很小的开销,但是IMO这比没有重复会更烦人。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM