[英]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);
}
這里有很多重復的bar
, baz
和quux
:
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.