c ++命名空间和类层次结构

时间:2013-05-05 12:57:40

标签: c++

这个问题困扰了我一段时间,但找不到处理它的最佳方法。我试图通过一个例子来证明这一点。

我正在开发一个包含许多类的图形库。有些类是“ ”的一部分关系,就像这三个类一样:

namespace MyGraphicsLibrary
{

class MatrixStack
{

};

class Transform
{
    MatrixStack mMatrixStack;
};

class Renderer
{
    Transform mTransform;
};

}

Renderer类供用户使用,但我不希望他们在查找Transform时看到MatrixStackMyGraphicsLibrary类。最后两个类仅适用于Renderer类,不适合用户使用。

我在这里尝试做两件事:

  1. 隐藏用户的TransformMatrixStack课程。

  2. 反映班级的“部分”层次结构。

  3. 我尝试了以下解决方法:

    1. 对我来说,最好的解决方案是私有嵌套类,因为它会向用户显示嵌套类是私有的,如果只是查看Renderer类声明,也会反映层次结构。以下帖子实际上让我不确定这是一个好的解决方案:Pros and cons of using nested C++ classes and enumerations?

    2. 我尝试将TransformMatrixStack放入另一个名为Private的命名空间中。因此,查找MyGraphicsLibrary命名空间的用户将看到Private命名空间仅覆盖所有不适合用户的类。 这很好,但是有很多其他类都有相同的问题,我很快就用Private命名空间填充了彼此无关的类。 在这里,我只能提出丑陋的解决方案,比如引入嵌套的命名空间:

      namespace MyGraphicsLibrary
      {
          //private classes belonging to Renderer class
          namespace PrivateRenderer
          {
          class MatrixStack
          {
          };
      
              class Transform
              {
                  MatrixStack mMatrixStack;
              };
          }
      
          //public classes for users
          class Renderer
          {
          Transform mTransform;
          };
      }
      
    3. 也许我在这里想念一些东西,但你觉得哪一个是要走的路。 有没有人有第三种方式?

3 个答案:

答案 0 :(得分:2)

你可以使用PIMPL-(也称为不透明指针)习语。 戴上帽子,您可以通过以下方式完全隐藏用户的类:

在您的公开标题中(在您的包含文件夹中): Renderer.h

class RendererImpl; // forward declaration of internal render structure

//public classes for users
class Renderer
{
  public:
    Renderer();
    ~Renderer();
    // public interface comes here and delegates all calls to RendererImpl (have to be implemented in cpp)

  RendererImpl* renderer; // better use something like QScopedPointer here
};

cpp:

#include "RendererImpl.h" // your actual renderer that 

Renderer::Renderer()
:renderer(new RendererImpl)
{}
Renderer::~Renderer()
{
  delete renderer;
}

可以从API完全隐藏实现。标头必须与真实接口分开。

答案 1 :(得分:1)

如果要将Transform存储为普通(非指针/引用)成员,那么对于公共头的编译,其定义也应该是可见的,因为它会影响容器类的布局。

因此,只要您想使用容器类,该类型就会显示。

您有以下选择:

  1. 通过命名表明它们不是公开使用的。通过放入命名空间(如boost中的详细信息),或者为其名称加前缀/后缀。
  2. 使用阻止客户端使用该类的技术。使每个成员函数都是私有的,并声明容器类的朋友。律师 - 客户成语是一种更复杂的细粒度访问控制方式。
  3. 间接存储转换(指针或引用),因此您不需要在公共标头中定义它。这是pimpl。如果公共类型是接口,则是实际Transform实现的基类的变体。
  4. 未命名的命名空间:在标题中绝对是个坏主意。未命名的命名空间类似于C static:它们获得编译器生成的标识符,该标识符保证对于给定的转换单元是唯一的。您最终将获得与您定义的许多不同地方一样多的不同变换类型。

答案 2 :(得分:0)

使用匿名命名空间:

namespace MyGraphicsLibrary
{
    namespace
    {
        class MatrixStack
        {

        };

        class Transform
        {
            MatrixStack mMatrixStack;
        };
    }

    class Renderer
    {
        Transform mTransform;
    };

}