2010-03-22 44 views
7

我想管理從共同容器中的共享接口類派生的類的一堆對象。將通用接口派生的類的對象存儲在公共容器中的最安全方法是什麼?

爲了說明問題,假設我正在構建一個包含不同演員的遊戲。我們稱之爲接口IActor,並從中推導出EnemyCivilian。現在

,這個想法是讓我的遊戲主循環能夠做到這一點:

// somewhere during init 
std::vector<IActor> ActorList; 
Enemy EvilGuy; 
Civilian CoolGuy; 
ActorList.push_back(EvilGuy); 
ActorList.push_back(CoolGuy); 

// main loop 
while(!done) { 
    BOOST_FOREACH(IActor CurrentActor, ActorList) { 
     CurrentActor.Update(); 
     CurrentActor.Draw(); 
    } 
} 

...或者類似的規定。這個例子顯然不起作用,但這正是我在這裏問的原因。

我想知道:在普通的異構容器中管理這些對象的最好,最安全,最高級別的方法是什麼?我知道各種各樣的方法(Boost :: Any,void *,帶boost :: shared_ptr的處理程序類,Boost.Pointer Container,dynamic_cast),但我無法決定哪種方法可以到達此處。

另外我想強調的是,我想盡可能遠離手動內存管理或嵌套指針。

非常感謝:)。

回答

3

正如你所猜測的,你需要將對象存儲爲指針。
我更喜歡使用boost指針容器(而不是普通的智能指針容器)。

其原因是boost ptr容器訪問對象,就好像它們是對象(返回引用)而不是指針。這使得在容器上使用標準仿函數和算法變得更加容易。

智能指針的缺點是您共享所有權。
這不是你真正想要的。你想要所有權在一個地方(在這裏是容器)。

boost::ptr_vector<IActor> ActorList; 
ActorList.push_back(new Enemy()); 
ActorList.push_back(new Civilian()); 

std::for_each(ActorList.begin(), 
       ActorList.end(), 
       std::mem_fun_ref(&IActor::updateDraw)); 
+0

喜歡你使用for_each的方式 – 2010-03-22 17:37:28

+0

你能解釋一下你的for_each以及如何使用BOOST_FOREACH來使用它嗎? – Svenstaro 2010-03-23 03:08:48

+0

std :: for_each(I1,I2,Action)。將Action應用於I1和I2(不包括I2)範圍內的所有值(在本例中調用方法updateDraw)。 I1,I2是迭代器。請參閱:http://www.sgi.com/tech/stl/for_each.html – 2010-03-23 14:55:04

4

我的即時反應是你應該在容器中存儲智能指針,並確保基類定義了足夠的(純粹的)虛擬方法,你永遠不需要返回派生類dynamic_cast

10

爲了解決你所提到的問題,儘管你正朝着正確的方向前進,但是你做錯了方向。這就是你需要做的

  • 定義一個基類(你已經這樣做),與將被派生類Enemy,並在你的情況Civilian覆蓋虛函數的東西。
  • 你需要選擇一個合適的容器來存儲你的對象。你已經採取了std::vector<IActor>這不是一個好的選擇,因爲
    • 首先,當您向矢量添加對象時,它會導致對象切片。這意味着只有IActor部分的EnemyCivilian被存儲而不是整個對象。
    • 其次,您需要根據對象的類型(virtual functions)調用函數,該函數只有在使用指針時纔會發生。

上下兩個點的理由,你需要使用它可以包含指針,像std::vector<IActor*>容器的事實。但更好的選擇是使用container of smart pointers,這可以讓你免受內存管理的困擾。您可以使用任何智能指針根據您的需要(而不是auto_ptr

這是你的代碼是什麼樣子

// somewhere during init 
std::vector<some_smart_ptr<IActor> > ActorList; 
ActorList.push_back(some_smart_ptr(new Enemy())); 
ActorList.push_back(some_smart_ptr(new Civilian())); 

// main loop 
while(!done) 
{ 
    BOOST_FOREACH(some_smart_ptr<IActor> CurrentActor, ActorList) 
    { 
     CurrentActor->Update(); 
     CurrentActor->Draw(); 
    } 
} 

這是非常相似您的原始代碼除了智能指針部分

+3

具體情況,你想拷貝語義的智能指針 – 2010-03-22 13:32:32

+0

燁我與 – 2010-03-22 13:45:05

+1

同意我喜歡你的方法,因爲它看起來很乾淨基於一個方法的指針。然而,正如其他人指出的那樣,我想要做的事情可能會更好地使用Boost指針容器來完成,因爲它們確實看起來正是爲了我想要達到的目的而製作的。如果失敗,我會嘗試你的方法。 或者你看到任何理由*不*使用升壓指針容器? – Svenstaro 2010-03-23 03:12:24

3

如果您希望容器獨佔擁有其中的元素,請使用Boost指針容器:它們是專爲這項工作而設計。否則,請使用shared_ptr<IActor>的容器(並且當然要正確使用它們,這意味着需要共享所有權的每個人都使用shared_ptr)。

在這兩種情況下,請確保IActor的析構函數是虛擬的。

void*要求你做手動內存管理,這樣就沒有了。 Boost.Any在類型通過繼承關聯時是過度的 - 標準多態是完成這項工作的。

無論您是否需要dynamic_cast都是一個正交問題 - 如果容器的用戶只需要IActor接口,並且您(a)使接口的所有功能都爲虛擬,否則(b)使用非 - 虛擬接口習慣用法,那麼你不需要dynamic_cast。如果容器的用戶知道一些IActor對象是「真正的」平民,並且想要利用民間界面而不是IActor的東西,那麼你需要施放(或重新設計)。

相關問題