C++:无法弄清楚如何正确隐藏实现细节
C++: Can't figure out how to hide implementation details properly
我有以下设置:
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
的私有嵌套 class 会使 foo.cpp
中的各种帮助-class 失败,因为 "A is private".
那么解决这个问题最干净的方法是什么?
解决方案
我最终采用了@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!
}
私有的不是A
的构造函数,而是整个class。
最好的解决方案是创建一个 "private" 命名空间。 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
的成员。这以更深的嵌套为代价提供了 "real" 访问保护。我个人更喜欢第一种解决方案,避免嵌套 classes.
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
的帮助者都需要成为朋友。有多种解决方法,例如在具有 protected
访问权限的基础 class 中嵌套 A
,但实际上,namespace impl
提供的妥协最少。
恕我直言,您有两种选择。您可以 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 并使用它的内容。
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 惯用语也可以使用智能指针,我只是为了 clarity/brevity 而没有在这里。
如果 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*
也是一种非标准的前向声明方法。当然,使用智能指针可以减少此位置内存泄漏的可能性。
我有以下设置:
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
的私有嵌套 class 会使 foo.cpp
中的各种帮助-class 失败,因为 "A is private".
那么解决这个问题最干净的方法是什么?
解决方案
我最终采用了@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!
}
私有的不是A
的构造函数,而是整个class。
最好的解决方案是创建一个 "private" 命名空间。 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
的成员。这以更深的嵌套为代价提供了 "real" 访问保护。我个人更喜欢第一种解决方案,避免嵌套 classes.
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
的帮助者都需要成为朋友。有多种解决方法,例如在具有 protected
访问权限的基础 class 中嵌套 A
,但实际上,namespace impl
提供的妥协最少。
恕我直言,您有两种选择。您可以 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 并使用它的内容。
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 惯用语也可以使用智能指针,我只是为了 clarity/brevity 而没有在这里。
如果 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*
也是一种非标准的前向声明方法。当然,使用智能指针可以减少此位置内存泄漏的可能性。