避免在C ++类中泄露外部类型

时间:2011-10-13 12:40:16

标签: c++ pimpl-idiom

我在标题中定义了一个类(缩写):

class CairoRenderer
{
public:
    CairoRenderer();
    ~CairoRenderer();
...
protected:
    cairo_t* m_context;
    cairo_surface_t* m_surface;
};

cairo_t和cairo_surface_t是Cairo图形库定义的类型。

我遇到的问题是,如果我想从另一个库或应用程序中使用此类或其派生类,我还需要包含cairo标头,因为我通过CairoRenderer标头“泄漏”了cairo类型。我希望同一个库中的这个类(或它的任何子类)可以在外部使用,而不需要包含cairo头或链接到cairo库。

所以我接下来尝试的是按照Wikipedia示例使用pimpl技术,因为看起来它可以做我想要实现的目标:

CairoRenderer.h(缩写)

class CairoRenderer
{
...
protected:
    struct CairoRendererImpl;
    CairoRendererImpl* m_pimpl;
};

CairoRenderer.cpp(缩写)

#include "CairoRenderer.h"
#include "cairo.h"

....

struct CairoRenderer::CairoRendererImpl 
{
public:
    CairoRendererImpl() : m_surface(NULL), m_context(NULL) { }
    ~CairoRendererImpl() 
    {
        cairo_surface_destroy(m_surface);
        cairo_destroy(m_context);
    }

    void Initialize(cairo_t* context, cairo_surface_t* surface)
    {
        m_context = context;
        m_surface = surface;
    }

    cairo_surface_t* surface() { return m_surface; }
    cairo_t* context() { return m_context; }

private:
    cairo_surface_t* m_surface;
    cairo_t* m_context;
};

CairoRenderer::CairoRenderer() : m_pimpl(new CairoRendererImpl()) { ... }

CairoRenderer::~CairoRenderer() { delete m_pimpl; }

我遇到的问题是当我尝试从派生类访问m_pimpl成员时,我收到编译器错误:

error C2027: use of undefined type 'CairoRenderer::CairoRendererImpl'

我做错了吗?或者我想做什么甚至可能?

3 个答案:

答案 0 :(得分:4)

你正在使用pimpl成语,这是你的问题。您已从所有外部代码隐藏了CairoRendererImpl的定义,包含派生类中的代码。

首先,我想质疑拥有一个具有数据成员和非虚拟析构函数的基类的价值。您应该查看要继承CairoRenderer的基本原因,并考虑替代解决方案。

如果您想要支持子类化,但仅限于库中的类,那么您应该将CairoRendererImpl的定义放入共享头文件中,该文件可以包含在内。 CairoRenderer heirachy中所有相关类的实现文件。

答案 1 :(得分:1)

pimpl成语用得好。

避免必须包含cairo.h文件的方法是在CairoRenderer.h文件中包含语句(即forward declaration

class CairoRendererImpl;

然后离开它; 包含CairoRendererImpl.h,因为这将需要拖动cairo.h的定义。

你可以这样做,因为CairoRenderer只需要一个指针来实现 - 只要你实际上没有从头文件调用任何方法(例如内联函数),编译器就不需要看到完整声明CairoRendererImpl类。

在CairoRenderer.cc中,你可以包含CairoRendererImpl.h文件(它将获得cairo.h的东西,但此时你真正想要/需要的东西)。

HTH, ħ

答案 2 :(得分:0)

我认为struct CairoRenderer::CairoRendererImpl是错误的。由于您不使用名称空间,因此像这样的简单定义就足够了:

struct CairoRendererImpl 
{
    ...
}

此外,您必须稍微调整一下CairoRenderer课程:

struct CairoRendererImpl;

class CairoRenderer
{
...
protected:
    CairoRendererImpl* m_pimpl;
};