【问题标题】:c++ namespace and class hierarchyc++ 命名空间和类层次结构
【发布时间】:2013-05-05 12:57:40
【问题描述】:

这是一个困扰我一段时间的问题,但找不到最好的解决方法。我试图通过一个例子来说明这一点。

我正在开发一个包含许多类的图形库。一些类之间是“part of”的关系,比如这 3 个类:

namespace MyGraphicsLibrary
{

class MatrixStack
{

};

class Transform
{
    MatrixStack mMatrixStack;
};

class Renderer
{
    Transform mTransform;
};

}

Renderer 类是供用户使用的,但我不希望他们在查找 MyGraphicsLibrary 时看到 TransformMatrixStack 类。最后两个类仅供Renderer类使用,不供用户使用。

我在这里尝试做两件事:

  1. 对用户隐藏 TransformMatrixStack 类。

  2. 反映类的“part-of”层次结构。

我尝试了以下方法来解决这个问题:

    1234563以下帖子实际上让我不确定这是一个好的解决方案:Pros and cons of using nested C++ classes and enumerations?
  1. 我尝试将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;
        };
    }
    

也许我在这里错过了一些东西,但你认为哪一个是要走的路。 有人有第三种方式吗?

【问题讨论】:

    标签: c++


    【解决方案1】:

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

    在您的公共标头中(在您的包含文件夹中): 渲染器.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 完全隐藏。标头必须与实际接口分开。

    【讨论】:

      【解决方案2】:

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

      因此,无论您想在哪里使用容器类,都可以看到该类型。

      您有以下选择:

      1. 通过命名表明它们不供公众使用。通过放入命名空间(如 boost 中的详细信息),或为其名称添加前缀/后缀。
      2. 使用阻止客户端使用该类的技术。将每个成员函数设为私有并声明容器类友元。律师-客户惯用语是一种更复杂的细粒度访问控制方式。
      3. 间接存储Transform(指针或引用),因此您不需要在公共标头中定义它。这是粉刺。如果公共类型是接口(实际 Transform 实现的基类),则这是一种变体。

      未命名的命名空间:在标题中绝对是个坏主意。未命名的命名空间类似于 C 静态:它们获得编译器生成的标识符,该标识符保证对于给定的翻译单元是唯一的。您最终会得到与包含其定义的位置一样多的不同 Transform 类型。

      【讨论】:

      • 谢谢,我将此标记为答案,因为它提供了更多选择。我之前读过关于 pimpl 的文章,但似乎我必须深入研究它。
      【解决方案3】:

      使用匿名命名空间:

      namespace MyGraphicsLibrary
      {
          namespace
          {
              class MatrixStack
              {
      
              };
      
              class Transform
              {
                  MatrixStack mMatrixStack;
              };
          }
      
          class Renderer
          {
              Transform mTransform;
          };
      
      }
      

      【讨论】:

        猜你喜欢
        • 2015-02-15
        • 2023-04-10
        • 2016-03-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多