2013-07-21 36 views
0

Pimpl是「指向實現指針」的簡稱,並提供了一種方便的方式來隱藏類中的實現。我實現一個窗口類,它隱藏從這個類的用戶特定於平臺的功能和結構,因此類接口最終看起來挺乾淨的:pimpl的類可以使用pimpl的對象嗎?

class Window{ 
public: 
    /// Constructors & destructors: 
    Window(void); 
    Window(Window const& window_); 
    Window(Window&& window_); 
    explicit Window(std::string const& title_); 
    explicit Window(std::string&& title_); 
    ~Window(void); 
    /// Member data: 
    bool visible = false; 
    ContextGraphics graphics_context; 
    std::array<unsigned long, 2> position = {{0}}, size = {{800, 600}}; 
    std::string title; 
    /// Member functions: 
    void apply_changes(void); 
    Window& center_position(void); 
    bool enabled(void) const; 
    void update(void); 
    /// Member functions (overloaded operators, assignment): 
    Window& operator=(Window const& window_); 
    Window& operator=(Window&& window_); 
private: 
    /// Inner classes: 
    class Helper; 
    /// Member data: 
    std::unique_ptr<Window::Helper> _m_opHelper; 
}; 

幕後都是那些討厭的WINAPI調用等等,當我可能瞄準更廣泛的支持平臺時,類頭不需要改變,只需要源文件。非常有用,不需要那麼多重寫!

然而,這個對象似乎是(類內)我的問題:

ContextGraphics graphics_context; 

它是一個Direct3D/OpenGL圖形上下文(用戶 「ContextGraphics」 的可以確定),併爲你可能已經猜到了,它使用的平普爾 - 成語還有:

class ContextGraphics{ 
    /// Friends: 
    friend class Window; 
public: 
    /// Enumerations: 
    enum class API : unsigned int{ 
     API_DEFAULT, 
     API_DIRECT3D, 
     API_OPENGL 
    }; 
    /// Constructors & destructors: 
    ContextGraphics(void); 
    explicit ContextGraphics(ContextGraphics::API const& api_); 
    explicit ContextGraphics(ContextGraphics::API&& api_); 
    ~ContextGraphics(void); 
    /// Member data: 
    ContextGraphics::API api = ContextGraphics::API::API_DEFAULT; 
    unsigned int color_depth : 6; // 2^6 = 64 
    /// Member functions: 
    bool api_available(void) const noexcept; 
    bool enabled(void) const; 
    /// Member functions (overloaded operators, assignment): 
    ContextGraphics& operator=(ContextGraphics const& context_graphics_); 
    ContextGraphics& operator=(ContextGraphics&& context_graphics_); 
private: 
    /// Constructors & destructors: 
    ContextGraphics(ContextGraphics const& context_graphics_); 
    ContextGraphics(ContextGraphics&& context_graphics_); 
    /// Inner classes: 
    class Helper; 
    /// Member data: 
    std::unique_ptr<ContextGraphics::Helper> _m_opHelper; 
    /// Member functions: 
    void create(void); 
    void destroy(void); 
}; 

我現在面臨的問題是編譯器:

Window.cpp|145|error: invalid use of incomplete type 'class ContextGraphics::Helper'|

ContextGraphics.hpp|43|error: forward declaration of 'class ContextGraphics::Helper'|

編譯器似乎找不到類「ContextGraphics :: Helper」的實現,它只會引發如果一個帶有pimpl的類可以使用帶有pimpl的對象的問題。如果不將所有的實現放在Window類的源文件中,這可能會做什麼?對我來說,這似乎不是一個合理的解決方案。

+2

你有一個普通的週期性依賴問題。關於這個網站和其他網站的信息有很多。 Pimpl與它無關。 – Mat

+0

@Mat:哦,很高興爲這個問題命名。謝謝!這有助於找到可能的解決方案,如果確實存在的話。 – Helixirr

+0

那麼,問題仍然存在 - 這是可能的,如果是這樣,如何? – Helixirr

回答

0

這是可能的,但有點複雜。

首先,您應該使用像ContextGraphics等類的指針。

因此,該領域會像

ContextGraphics* graphics_context; 

而且它的作用與之前的Windows類預先聲明,這樣

class ContextGraphics; // forward decl 

class Window{ 
public: 
... 
}; 

現在,爲什麼這是有必要使用一個指針,以及編譯器真正需要的東西: 編譯器請參閱「ContextGraphics graphics_context;」它會嘗試將該字段「印刷」到Windows類中。要做到這一點,編譯器應該知道sizeof(ContextGraphics),它只在類完全定義時才知道。

這就是爲什麼以下是不可能的:

class Aaa; 
class Bbb; 

class Aaa 
{ 
int x; 
Bbb b; 
}; 

class Bbb 
{ 
Aaa a; 
int y; 
}; 

(試想一下:假設的sizeof(AAA)爲100,那麼的sizeof(BBB)中的sizeof(AAA)+4爲104,但如果是這樣,sizeof(Aaa :: a)也是104,所以sizeof(Aaa)真的是108--這就是圈子真正的地方。)

現在,使用指針(ContextGraphics * graphics_context;)問題得到解決,因爲編譯器知道它的大小,它對於任何指針都是不變的。

所以,下面的工作:

class Aaa; 
class Bbb; 

class Aaa 
{ 
int x; 
Bbb* b; 
}; 

class Bbb 
{ 
Aaa* a; 
int y; 
}; 

(的sizeof(AAA)== 8和sizeof(BBB)== 8,對於32位系統)

事實上pimplIdiom本身需要的最因爲這是編譯器在不知道類本身的情況下知道字段大小(或函數參數的大小)的唯一方法。

順便說一下(offtopic),你正在建立一個已經存在的庫 - 稱爲Qt,這可能會爲你節省很多時間。如果是關於gamedev - Qt也提供相當不錯的性能。