3

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

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

namespace MyGraphicsLibrary
{

class MatrixStack
{

};

class Transform
{
    MatrixStack mMatrixStack;
};

class Renderer
{
    Transform mTransform;
};

}

该类是供用户使用的,Renderer但我不希望他们在查找. 最后两个类仅供上课使用,不供用户使用。TransformMatrixStackMyGraphicsLibraryRenderer

在这里,我正在尝试做两件事:

  1. 对用户隐藏Transform,MatrixStack类。

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

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

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

  2. 我试图将Transform,MatrixStack放入另一个名为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;
        };
    }
    

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

4

3 回答 3

2

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

在您的公共标头中(在您的包含文件夹中):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
};

人民党:

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

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

这些实现可能对 API 完全隐藏。标头必须与实际接口分开。

于 2013-05-05T13:37:40.407 回答
1

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

因此,无论您想在哪里使用容器类,该类型都是可见的。

您有以下选择:

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

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

于 2013-05-05T14:04:13.277 回答
0

使用匿名命名空间:

namespace MyGraphicsLibrary
{
    namespace
    {
        class MatrixStack
        {

        };

        class Transform
        {
            MatrixStack mMatrixStack;
        };
    }

    class Renderer
    {
        Transform mTransform;
    };

}
于 2013-05-05T13:36:28.843 回答