C++:无法弄清楚如何正确隐藏实现细节
Posted
技术标签:
【中文标题】C++:无法弄清楚如何正确隐藏实现细节【英文标题】:C++: Can't figure out how to hide implementation details properly 【发布时间】:2015-11-03 18:42:26 【问题描述】:我有以下设置:
foo.h
:
class A
friend class B;
private:
A()
;
class B
public:
void addObject(Object &o); // adds to myMember; A is not exposed!
void computeResult(Result &r); // uses myMember to compute result
private:
vector<A> myMember;
;
A
的对象永远不会暴露给包括foo.h
在内的任何程序。带有A
的向量仅用于帮助B
发挥其计算适配器的作用。通过将A
的构造函数设为私有,我想我可以避免其他编译单元使用它,而且它似乎可以工作。但是问题出在
foo.cpp
void B::computeResult(Result &r)
MyCustomStorage<A> storage;
A *a = storage.allocate(); // error: "A::A() is private"
MyCustomStorage
的一部分看起来像这样:
template <typename T>
class MyCustomStorage
T *allocate()
...
T *ptr = new T[count]; // error: "A::A() is private"
...
;
但我认为既然allocate()
是从成员函数调用的,这不会发生!我该如何解决这个问题?
让A
成为MyCustomStorage
的朋友似乎非常意大利面。将A
设为B
的私有嵌套类会使foo.cpp
中的各种帮助类失败,因为“A 是私有的”。
那么解决这个问题的最干净的方法是什么?
解决方案
我最终选择了@potatoswatter 的第二个解决方案,并进行了以下适当的更改:
foo.h
class B
public:
void addObject(Object &o); // adds to myMember; A is not exposed!
void computeResult(Result &r); // uses myMember to compute result
private:
class A
private:
A()
;
class Helper; // forward declared!
vector<A> myMember;
;
foo.cpp
class B::Helper
int help(A& a) return 42; // no problem! Helper is a member of B
void B::computeResult(Result &r)
MyCustomStorage<A> storage;
A *a = storage.allocate(); // no problem! A is a member of B
Helper h;
h.help(*a); // no problem!
【问题讨论】:
见***.com/questions/8972588/… 让B
成为A
的朋友似乎很好。
【参考方案1】:
私有的不是A
的构造函数,而是整个类。
最好的解决方案是创建一个“私有”命名空间。 C++ 没有命名空间级别的访问保护,但可以合理地预期用户不会访问不熟悉的命名空间。
namespace impl
struct A
A()
;
class B
public:
void addObject(Object &o); // adds to myMember; A is not exposed!
void computeResult(Result &r); // uses myMember to compute result
private:
vector<impl::A> myMember;
;
另一种方法是让A
成为B
的成员。这以更深的嵌套为代价提供了“真正的”访问保护。我个人更喜欢第一种解决方案,并避免嵌套类。
class B
public:
void addObject(Object &o); // adds to myMember; A is not exposed!
void computeResult(Result &r); // uses myMember to compute result
private:
struct A
A()
;
vector<A> myMember;
;
任何需要A
的助手都需要成为朋友。有多种解决方法,例如将 A
嵌套在具有 protected
访问权限的基类中,但实际上,namespace impl
提供的妥协最少。
【讨论】:
我更喜欢第二个。客观上更好,因为它封装了A
,而第一个版本没有。我不希望人们不访问不熟悉的命名空间,namespace std;
曾经不熟悉,但仍然每个人都是 using
。
@nwp 但是像std::__1
和std::__function
这样的命名空间是陌生的。包括所有标准库实现、Boost 等在内的大型流行库往往更喜欢第一种方法,否则不会为了访问保护而扭曲类。
@potatoswatter 我使用了第二种解决方案,在我的第一篇文章中进行了编辑。我很想知道您是否提到了扭曲问题。
@bombax 当然可以。如果有任何“扭曲”,那只是B
正在捕获附加功能,就好像它是一个命名空间一样。如果出现问题,您可以随时重构嵌套类。【参考方案2】:
恕我直言,您有几个选择。您可以 1) 使用 Pimpl 成语,或者,2) 您可以使用前向声明。
Pimpl 成语例子:
class B
public:
void addObject(Object &o); // adds to myMember; A is not exposed!
void computeResult(Result &r); // uses myMember to compute result
private:
class Impl;
Impl *pimpl;
;
在您的 *.cpp 文件中,您可以定义 Impl
类并使用它。
class B::Impl
public:
std::vector<A> stuff;
B::B() : pimpl(new Impl)
B::~B()
delete pimpl;
void B::AddObject(Object &o)
pimpl->stuff.Fx(o);
您也可以为 Pimpl 习语使用智能指针,我只是为了清晰/简洁而没有在这里。
如果A
与B
在同一个命名空间中,也可以使用前向声明
class B
public:
void addObject(Object &o); // adds to myMember; A is not exposed!
void computeResult(Result &r); // uses myMember to compute result
private:
std::vector<class A*> myMember;
;
但是这个习惯用法与您的要求根本不同,并限制您在对象myMember
中使用指针,您可能不想这样做。内联定义class A*
也是一种非标准的前向声明方法。当然,使用智能指针会降低该位置内存泄漏的可能性。
【讨论】:
vector<class A*>
在功能上与vector<A>
不同,它是一种容易发生内存泄漏的反模式。 vector<class A>
和不完整的class A
将在实践中起作用,并且它被定为标准化,但尚未正式祝福。
@Potatoswatter 我注意到vector<class A*>
是根本不同的。我将对非标准的前向声明方法添加警告。感谢您的评论。
他可以使用带有std::unique_ptr
的部分特化来使用pimpl idiom并避免原始指针可能出现的内存泄漏。以上是关于C++:无法弄清楚如何正确隐藏实现细节的主要内容,如果未能解决你的问题,请参考以下文章