隐藏一个 C++标头中的类而不使用未命名的命名空间
我正在编写一个 C++ 标头,其中定义了一个
class A {
// ...
};
我想对外界隐藏的标头(因为它可能会在该标头的未来版本中更改甚至被删除)。
在同一个标头中还有一个类 B,它有一个类 A 的对象作为成员:
class B {
public:
// ...
private:
A a_;
};
从外部世界隐藏类 A 的正确方法是什么?
如果我将 A 的定义放在未命名的命名空间中,编译器会发出警告,因此我认为,由于内部链接问题,我应该做其他事情。
I am writing a C++ header in which I define a
class A {
// ...
};
that I would like to hide from the outside world (because it may change or even be removed in future versions of this header).
There is also a class B in the same header that has an object of class A as a member:
class B {
public:
// ...
private:
A a_;
};
What is a proper way of hiding class A from the outside world?
If I put the definition of A in an unnamed namespace, the compiler issues a warning, so I assume that, due to issues with internal linkage, I should do something else.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
在 C++ 中进行此操作的正确方法是
PIMPL
惯用法。另一种解决方案是将要隐藏的类放入嵌套命名空间中,该命名空间通常称为detail
。但这并不会使它完全私有,因为用户仍然会接触到它的依赖项,并且能够直接使用它。The right way to go about it in C++ is
PIMPL
idiom. Alternative solution is to put the class you want to hide into a nested namespace, which is usually calleddetail
. But that will not make it totally private as users will still be exposed to its dependencies, and will be able to use it directly.你可以做一个内部类:
You could do an inner class:
记录此类不是公共 API 的一部分,不应使用。
在 C++ 中,您必须信任与库代码链接的程序,因为您别无选择。 C++ 具有有限的“访问控制”功能,其中许多功能可以被绕过或滥用,因此您最好尊重 API 客户端并建立信任。
如果您将 API 设计得易于正确使用,并且难以无意中错误使用,那么您将帮助您的客户,如果您的客户滥用您的界面,那也不是您的错。
Document that this class is not part of the public API and should not be used.
In C++ you have to trusted programs that link with your library code because you have little other choice. C++ has limited "access control" features many of which can be bypassed or abused so you're better of treating your API clients with respect and building trust.
If you design your API to be easy to use correctly and hard to use unintentionally incorrectly then you will be helping your clients and it is hardly your fault if your clients abuse your interface.
无论如何,未命名的命名空间是没有用的,因为它只能保护多个定义。您可以做的是使用 pImpl Idiom(如其他答案中所述),或者使用
detail
命名空间。对于 Boost 来说效果很好:任何人在
detail
命名空间中搞乱东西都是自找麻烦。或者可能更加模糊它现在任何接触里面任何东西的人都应该被射中脚。 :)
An unnamed namespace is useless anyways, as it only protects agains multiple definitions. What you could do is either using the pImpl Idiom, as mentioned in other answers, or use a
detail
namespace. Works fine for Boost:Anyone messing with stuff in a
detail
namespace is asking for trouble. Or maybe obscure it even moreAnyone who now touches anything inside should be shot in the foot. :)
不要让
B 类
持有A
对象,而是让它持有A*
(或shared_ptr
) > 或unique_ptr
等)。这样
B类
只需要A类
的前向声明,并且A类
可以在B类
内部完全定义> 的源文件。Instead of
class B
holding anA
object, have it hold anA*
instead (or ashared_ptr<A>
, or anunique_ptr<A>
, etc.). This wayclass B
only needs a forward declaration ofclass A
andclass A
can be fully defined inside ofclass B
's source file.如果 A 是 B 的实现细节,则根本不要将其定义放在标头中。而是:
然后将A的定义放在B实现(即.cpp)文件中。
If A is an implementation detail of B, don't put its definition in the header at all. Instead:
and then put the definition of A in the B implementation (i.e. .cpp) file.
我想在 https://stackoverflow.com/a/5780976/1525238 上添加一个小增量,这对我有帮助更好地解决我的特殊用例,即“主”类是一个模板,“帮助/内部”类也必须是一个模板1。
我使用了一个名为
detail
的嵌套命名空间,将所有“helper”内容设为私有,并使“main”类成为“helper”类的friend
:私有内容是 < code>static 上面只是为了让代码更短。它们很可能与
Helper
实例相关联。回想起来,当然可以有更优雅的解决方案,涉及更少的黑魔法,但这在很大程度上取决于具体的应用。我仍然发现上述对于
friend
类来说是一个合法且不错的用例。1 这是因为我需要使用需要部分专业化的模板辅助函数,这是尚不允许的在 C++ 中,没有特殊原因,但技术上可以使用包装类。为了简单起见,上面省略了部分特化。
I'd like to add a small increment over https://stackoverflow.com/a/5780976/1525238 that helped me better solve my peculiar use case, namely where the "main" class is a template and the "helper/inner" class also has to be a template1.
I used a nested namespace called
detail
, made all "helper" content private and made the "main" class afriend
of the "helper" class:The private stuff is
static
above only to make the code shorter. They may very well be tied to theHelper
instance.In retrospect, there could certainly be more elegant solutions involving less black magic, but it highly depends on the specific application. I still find the above a legit, nice use case for a
friend
class.1 This is because I needed to use a template helper function that required a partial specialization, which is not allowed yet in c++, for no particular reason but is technically possible with a wrapper class. Partial specialization is omitted from the above for simplicity.