2012-11-06 88 views
10

是它在某種程度上可能隱藏實施完成以下任務:使用指針(PIMPL方法)

x.hpp - 這個文件是由許多其他類

class x_impl; //forward declare 
class x { 
    public: 
     //methods... 
    private: 
     x_impl* impl_; 
}; 

X包括的.cpp - 實施

#include <conrete_x> 
typedef concrete_x x_impl; //obviously this doesn't work 
//implementation of methods... 

所以基本上,我希望用戶包括文件x.hpp,但不知道conrete_x.hpp標題。

由於我只能通過指針使用concrete_x,它只能作爲私有數據成員出現,所以前向聲明應該足以讓編譯器知道爲它準備了多少空間。它看起來很像着名的「pimpl成語」。

你能幫助我嗎?

PS。我不想使用void*並將其左右投射。

+0

爲什麼你不能從x_impl繼承concrete_x? – StoryTeller

+0

你的問題到底是什麼? – Grizzly

+0

爲什麼你不想在中定義'class x_impl'本身? –

回答

9

事實上,它甚至可以從用戶完全隱藏:

// Foo.hpp 
class Foo { 
public: 

    //... 

private: 
    struct Impl; 
    Impl* _impl; 
}; 

// Foo.cpp 
struct Foo::Impl { 
    // stuff 
}; 

我只是想提醒你的是:

  • 你需要寫一個適當的析構函數
  • 因此,您還需要一個合適的拷貝構造函數,複製賦值運算符,移動構造函數和移動賦值運算符

有些方法可以自動化PIMPL,但需要花費一些黑魔法(與std::shared_ptr相似)。

+0

請看看@Bart van Ingen Schenau的想法。你更好的解決方案是什麼? – emesx

+1

@elmes:名字'Impl'完全隱藏,而Bart的解決方案在封閉的名字空間中引入了'x_impl'名字。它更隱藏着一個'private'嵌套結構。 –

+0

如果結構在cpp文件中的名稱空間內,這會工作嗎? – ChaoSXDemon

2

這隻有在前向聲明聲明類的實際名稱時纔有效。所以,要麼改變x.hpp到:

class concrete_x; 
class x { 
    public: 
     //methods... 
    private: 
     concrete_x* impl_; 
}; 

或使用該名稱x_impl在頭<concrete_x>定義的類。

0

這就是接口的用途。在共享頭文件中定義一個接口(純虛擬類)並將其提供給用戶。從接口繼承你的具體類,並把它放在非共享的頭文件中。在cpp文件中實現具體類(甚至可以在cpp中定義具體類)。

+0

這似乎沒問題,只有我將有一個虛擬課程,以隱藏私人成員的細節; C++荒謬。 – emesx

+0

@elmes,如果你想着不同的concrete_x的可能性,而不是最自然的方法...... – StoryTeller

4

作爲替代從@Angew答案,如果名稱concrete_x不應該被人知道的類x的用戶,你可以這樣做:

x.hpp

class x_impl; 
class x { 
    public: 
    x(); 
    ~x(); 
    //methods... 
    private: 
    x_impl* impl_; 
}; 

in x。CPP

#include <concrete_x> 
class x_impl : public concrete_x { }; 

x:x() : impl_(new x_impl) {} 
x:~x() { delete impl_; } 
+0

是的,這是解決方案。雖然它在C++ 11中不是通用的,因爲'concrete_x'可能是最終的。最後一個問題:爲了隱藏它,派生類的性能/內存成本是多少? – emesx

+0

與直接存儲指向concrete_x的指針相比,沒有性能/內存成本。維護人員需要了解它,只是(小)維護成本。 –

+0

你能解釋'concrete_x'發生了什麼嗎?示例頭文件丟失了,它不清楚爲什麼include包含在'x.cpp'中,而不是''concrete_x'的類定義添加到'x.cpp'的頂部。 – jww