简体   繁体   English

在不使用未命名命名空间的情况下在标头中隐藏 C++ 类

[英]Hiding a C++ class in a header without using the unnamed namespace

I am writing a C++ header in which I define a我正在编写一个 C++ 头文件,在其中定义了一个

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:在同一个头文件中还有一个类 B,它有一个类 A 的对象作为成员:

class B {
public:
   // ...

private:
   A a_;
};

What is a proper way of hiding class A from the outside world?向外界隐藏A类的正确方法是什么?

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.如果我将 A 的定义放在未命名的命名空间中,编译器会发出警告,因此我认为,由于内部链接问题,我应该做其他事情。

The right way to go about it in C++ is PIMPL idiom.在 C++ 中正确的方法是PIMPL idiom。 Alternative solution is to put the class you want to hide into a nested namespace, which is usually called detail .另一种解决方案是将要隐藏的类放入嵌套命名空间中,通常称为detail 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:你可以做一个内部类:

class B
{
  class A { /* ... */ };
  A a_;
}

Document that this class is not part of the public API and should not be used.记录此类不是公共 API 的一部分,不应使用。

In C++ you have to trusted programs that link with your library code because you have little other choice.在 C++ 中,您必须信任与您的库代码链接的程序,因为您别无选择。 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. C++ 具有有限的“访问控制”功能,其中许多功能可以被绕过或滥用,因此您最好尊重并建立信任来对待您的 API 客户端。

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.如果您将 API 设计为易于正确使用且难以误用,那么您将帮助您的客户,如果您的客户滥用您的界面,这几乎不是您的错。

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.您可以做的是使用 pImpl Idiom,如其他答案中所述,或者使用detail命名空间。 Works fine for Boost:适用于 Boost:

namespace detail{
  class A{
    // ...
  };
}

class B{
public:
  // ...
private
  A a_;
};

Anyone messing with stuff in a detail namespace is asking for trouble.任何在detail命名空间中弄乱东西的人都是在自找麻烦。 Or maybe obscure it even more或者也许更模糊它

namespace _b_impl_detail{
  // ...
};

Anyone who now touches anything inside should be shot in the foot.任何现在触碰里面任何东西的人都应该被射中脚。 :) :)

Instead of class B holding an A object, have it hold an A* instead (or a shared_ptr<A> , or an unique_ptr<A> , etc.).不是class B持有A对象,而是持有A* (或shared_ptr<A>unique_ptr<A>等)。 This way class B only needs a forward declaration of class A and class A can be fully defined inside of class B 's source file.这样class B只需要class A class A的前向声明, class A class A可以在class B的源文件中完全定义。

If A is an implementation detail of B, don't put its definition in the header at all.如果 A 是 B 的实现细节,则根本不要将其定义放在标题中。 Instead:反而:

class B {

   ...
   class A * myA;
};

and then put the definition of A in the B implementation (ie .cpp) file.然后把A的定义放在B实现(即.cpp)文件中。

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 template 1 .我想在https://stackoverflow.com/a/5780976/1525238上添加一个小增量,这有助于我更好地解决我的特殊用例,即“main”类是模板,“helper/inner”类也必须是模板1

I used a nested namespace called detail , made all "helper" content private and made the "main" class a friend of the "helper" class:我使用了一个名为detail的嵌套命名空间,将所有“helper”内容设为私有,并使“main”类成为“helper”类的friend

template<__MAIN_TEMPLATE_PARAMS__> class Main;

namespace detail {
    template<__HELPER_TEMPLATE_PARAMS__> class Helper {

        /* All Main templates are friends */
        template<__MAIN_TEMPLATE_PARAMS__> friend class Main; 

        /* Private stuff, not reachable from the outside */
        static void privateThing(){
            ...
        }
    };
}

template<__MAIN_TEMPLATE_PARAMS__> class Main {
    void usePrivateThing(){
        detail::Helper<__DESIRED_HELPER_TEMPLATE_PARAMS__>::privateThing();
    }
};

The private stuff is static above only to make the code shorter.上面的私有内容是static ,只是为了使代码更短。 They may very well be tied to the Helper instance.它们很可能与Helper实例相关联。

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.我仍然发现上面是一个合法的,很好的friend类用例。


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. 1这是因为我需要使用一个需要部分特化的模板辅助函数,这在 c++ 中不允许的,没有特别的原因,但技术上可以使用包装类。 Partial specialization is omitted from the above for simplicity.为简单起见,上面省略了部分专业化。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM