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

3

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

我正在开发一个带有许多类的图形库。其中一些类之间存在“部分”关系,就像这3个类:

namespace MyGraphicsLibrary
{

class MatrixStack
{

};

class Transform
{
    MatrixStack mMatrixStack;
};

class Renderer
{
    Transform mTransform;
};

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

这里我想做两件事:

  1. 隐藏 TransformMatrixStack 类,使其对用户不可见。

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

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

  1. The best solution for me would be the private nested-classes, as it would show the user that the nested class is private and also reflects the hierarchy if you simply look at the Renderer class declaration. The following post actually makes me uncertain that is good solution: Pros and cons of using nested C++ classes and enumerations?

  2. I tried to put Transform, MatrixStack into another namespace called Private. So user looking up MyGraphicsLibrary namespace would see Private namespace only covering all classes which are not for the users. That's good, but there are lot of other classes with the same issue, and i quickly fill the Private namespace with classes which has nothing to do with each other. Here I could only come up with ugly solutions, like introducing nested namespaces:

    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个回答

2
你可以使用PIMPL(也称为不透明指针)惯用语法。这样,你可以完全隐藏类,使用户无法访问,具体方法如下:
在你的公共头文件(在你的include文件夹中)中添加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
};

the cpp:

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

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

实现可以完全隐藏在API之后。头文件必须与实际接口分离。

1
如果您想将Transform存储为普通(非指针/引用)成员,则在编译公共头文件时,其定义也应该可见,因为它会影响容器类的布局。因此,无论何时想要使用容器类,都将看到该类型。
您有以下选项:
1. 通过命名来表明它们不适用于公共使用。可以通过将其放入命名空间(例如boost中的detail),或者在其名称前缀/后缀中添加标识符来实现。
2. 使用防止客户端使用该类的技术。将每个成员函数设置为私有,并声明容器类为友元。代理-客户端习语是一种更复杂的细粒度访问控制方式。
3. 间接存储Transform(指针或引用),因此您不需要在公共头文件中使用其定义。这就是pimpl。如果公共类型是实际Transform实现的接口基类,则是其变体。
未命名的命名空间:在头文件中肯定是个坏主意。未命名的命名空间类似于C静态:它们会得到一个由编译器生成的标识符,保证在给定的翻译单元中唯一。您最终将以包含其定义的位置数量为Transform类型的数量。

谢谢,我将此标记为答案,因为它提供了更多的选择。我之前读过有关pimpl的内容,但似乎还需要深入了解它。 - Avi

0

使用匿名命名空间:

namespace MyGraphicsLibrary
{
    namespace
    {
        class MatrixStack
        {

        };

        class Transform
        {
            MatrixStack mMatrixStack;
        };
    }

    class Renderer
    {
        Transform mTransform;
    };

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接