隐藏一个 C++标头中的类而不使用未命名的命名空间

发布于 2024-11-03 09:39:43 字数 334 浏览 7 评论 0原文

我正在编写一个 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

半城柳色半声笛 2024-11-10 09:39:43

在 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 called 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.

悲歌长辞 2024-11-10 09:39:43

你可以做一个内部类:

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

You could do an inner class:

class B
{
  class A { /* ... */ };
  A a_;
}
那一片橙海, 2024-11-10 09:39:43

记录此类不是公共 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.

清晨说晚安 2024-11-10 09:39:43

无论如何,未命名的命名空间是没有用的,因为它只能保护多个定义。您可以做的是使用 pImpl Idiom(如其他答案中所述),或者使用 detail 命名空间。对于 Boost 来说效果很好:

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

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

任何人在 detail 命名空间中搞乱东西都是自找麻烦。或者可能更加模糊它

namespace _b_impl_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:

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

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

Anyone messing with stuff in a detail namespace is asking for trouble. Or maybe obscure it even more

namespace _b_impl_detail{
  // ...
};

Anyone who now touches anything inside should be shot in the foot. :)

岁月静好 2024-11-10 09:39:43

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.). 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.

蔚蓝源自深海 2024-11-10 09:39:43

如果 A 是 B 的实现细节,则根本不要将其定义放在标头中。而是:

class B {

   ...
   class A * myA;
};

然后将A的定义放在B实现(即.cpp)文件中。

If A is an implementation detail of B, don't put its definition in the header at all. Instead:

class B {

   ...
   class A * myA;
};

and then put the definition of A in the B implementation (i.e. .cpp) file.

日暮斜阳 2024-11-10 09:39:43

我想在 https://stackoverflow.com/a/5780976/1525238 上添加一个小增量,这对我有帮助更好地解决我的特殊用例,即“主”类是一个模板,“帮助/内部”类也必须是一个模板1

我使用了一个名为 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();
    }
};

私有内容是 < 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 a friend of the "helper" class:

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. They may very well be tied to the Helper 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文