6

我正在编写一个 C++ 头文件,我在其中定义了一个

class A {
   // ...
};

我想对外界隐藏(因为它可能会在此标头的未来版本中更改甚至被删除)。

在同一个头文件中还有一个 B 类,它有一个 A 类的对象作为成员:

class B {
public:
   // ...

private:
   A a_;
};

向外界隐藏 A 级的正确方法是什么?

如果我将 A 的定义放在一个未命名的命名空间中,编译器会发出警告,所以我假设,由于内部链接的问题,我应该做其他事情。

4

7 回答 7

10

在 C++ 中进行此操作的正确方法是PIMPLidiom。另一种解决方案是将要隐藏的类放入嵌套命名空间中,通常称为detail. 但这不会使其完全私有,因为用户仍将暴露于它的依赖项,并且能够直接使用它。

于 2011-04-25T17:18:21.033 回答
5

你可以做一个内部类:

class B
{
  class A { /* ... */ };
  A a_;
}
于 2011-04-25T17:14:54.423 回答
2

无论如何,未命名的命名空间是无用的,因为它只能保护多个定义。您可以做的是使用 pImpl Idiom,如其他答案中所述,或者使用detail命名空间。适用于 Boost:

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

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

任何弄乱detail命名空间中的东西的人都是在自找麻烦。或者也许更加模糊它

namespace _b_impl_detail{
  // ...
};

任何现在触及内部任何东西的人都应该被射中脚。:)

于 2011-04-25T17:26:03.740 回答
2

记录此类不是公共 API 的一部分,不应使用。

在 C++ 中,您必须信任与您的库代码链接的程序,因为您别无选择。C++ 具有有限的“访问控制”功能,其中许多功能可以被绕过或滥用,因此您最好尊重您的 API 客户端并建立信任。

如果您将 API 设计为易于正确使用并且难以无意中错误地使用,那么您将帮助您的客户,如果您的客户滥用您的界面,这几乎不是您的错。

于 2011-04-25T17:20:57.540 回答
1

与其class B持有一个A对象,不如让它持有一个A*相反(或 a shared_ptr<A>,或 anunique_ptr<A>等)。这种方式class B只需要一个前向声明,class A并且class A可以在class B源文件中完全定义。

于 2011-04-25T17:15:29.227 回答
1

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

class B {

   ...
   class A * myA;
};

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

于 2011-04-25T17:17:50.243 回答
0

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

我使用了一个名为 的嵌套命名空间detail,将所有“帮助”内容设为私有,并将friend“帮助”类的“主”类设为 a:

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();
    }
};

上面的私有内容static只是为了使代码更短。它们很可能与Helper实例相关联。

回想起来,当然可以有更优雅的解决方案,涉及更少的黑魔法,但这在很大程度上取决于具体的应用程序。我仍然发现上面是一个合法的、很好的friend类用例。


1这是因为我需要使用需要部分特化的模板辅助函数,这在 c++ 中还不允许,没有特殊原因,但在技术上可以使用包装类。为简单起见,上面省略了部分特化。

于 2019-12-23T17:09:25.050 回答