在尝试从头文件中删除所有实现细节时,我决定使用并尝试PIMPL习惯用法。大部分(如果不是全部)示例,例如cppreference,我已经看到了间接使用级别,这是除了封装之外我无法理解的原因。这些示例始终带有这种风味模式。
典型
//A.hpp
class A
{
public:
A(int);
//More non-static class methods
void set(int);
private:
struct a_impl;
std::unique_ptr<struct a_impl> pimpl;
};
//A.cpp
struct A::a_impl
{
private:
int i;
public:
a_impl(int i) : i{i}{}
//More non-static implementation class methods
void set(int i) {this->i = i;}
};
A::A(int i) : std::make_unique<struct a_impl>(i) {}
A::set(int i) {pimpl->set(i);} //????
A:://More indirect calls to non-static member functions through pointer
在某些时候,我开始想知道,如果我们谈论的是实现,为什么我需要所有这些级别的复杂性和间接性。没有所有这些间接调用,为什么不做最简单的事情呢?
为什么不呢?
//A.hpp
class A
{
public:
A(int);
//.....
void set(int);
private:
struct a_impl;
std::unique_ptr<struct a_impl> pimpl;
};
//A.cpp
struct A::a_impl
{
int i;
}
A::A(int i) : std::make_unique<struct a_impl>() { pimpl->i = i; }
A::set(int i) { pimpl->i = i; } //!!!!
我想澄清一下:
1-是否所有这些样本都是出于教育和良好封装实践的目的而提出的?
2- 除了问题1之外,我是否还缺少其他充分的理由来增加这种级别的复杂性和开销?
3-或者我提出的替代方案不是PIMPL惯用语吗?
答案 0 :(得分:4)
您自己的方法以及与之比较的方法只是两种不同的习惯用法。 Herb Sutter在GotW #100中列出了它们以及更多内容:
该类的哪些部分应该放入impl对象?有潜力 选项包括:
- 将所有私有数据(但不是函数)放入impl;
- 将所有私人成员纳入展示;
- 将所有私有成员和受保护成员都加入了展示;
- 将所有非虚拟私人成员纳入impl;
- 将所有内容放入impl中,并将公共类本身编写为仅公共接口,每个接口都实现为简单的转发功能 (一个手柄/主体变体)。
每个都有其优点和缺点。对于您研究的情况,您的方法似乎更好。对于其他情况,具有行为而不只是状态的pimpl可能更合适。真的没有一种适合所有人。