2012-01-16 69 views
4

我寫了一個使用虛函數實現多態的程序。我有一個主要的User類,它盲目地調用它認爲是通用對象的方法(儘管它們實際上應該是專用的)。這些對象來自覆蓋其基類中純虛函數的類。下面的代碼調整應當證明我的設置:多態實現問題

在BaseConfig.h泛型類(BaseConfig):

class BaseConfig { 
public: 
    ... 
    virtual void display() const = 0; 
    ... 
} 

上述泛型類(SpecialConfig)的SpecialConfig.h專門版本:

class SpecialConfig : public BaseConfig { 
public: 
    ... 
    void display() const; 
    ... 
} 

以上專業類的SpecialConfig.cpp實施:

... 
void SpecialConfig::display() const { 
    // print some text 
} 
... 

現在,當我創建BaseConfig指針並將其設置爲SpecialConfig對象的地址時,調用display()將按照它的設想擊中SpecialConfig類的display()函數。但是,與以下代碼片段中我所期望的不同的是,出於某種原因,在SpecialConfig對象返回BaseConfig隊列後,對它們調用display()函數不再會觸發SpecialConfig中的display()函數而是嘗試在BaseConfig中使用display()函數,導致程序退出。

這是一個用於生成配置排列的通用類。我們叫它BaseRuleSet在BaseRuleSet.h:

class BaseRuleSet { 
public: 
    ... 
    virtual queue<BaseConfig *> getValidChildConfigurations(BaseConfig * c) const = 0; 
    ... 
} 

其getValidChildConfigurations功能將在專用規則集類中重寫如圖所示類SpecialRuleSet從SpecialRuleSet.h:

class SpecialRuleSet : public BaseRuleSet { 
public: 
    ... 
    queue<BaseConfig *> getValidChildConfigurations(BaseConfig * c) const; 
} 

的實施在SpecialRuleSet.cpp上面的類:

... 
queue<BaseConfig *> SpecialRuleSet::getValidChildConfigurations(BaseConfig * c) const { 

    queue<BaseConfig *> validChildConfigurations; 

    BaseConfig * baseConfigA; 
    BaseConfig * baseConfigB; 

    SpecialConfig specialConfigA; 
    SpecialConfig specialConfigB; 

    baseConfigA = &specialConfigA; 
    baseConfigB = &specialConfigB; 

    validChildConfigurations.push(baseConfigA); 
    validChildConfigurations.push(baseConfigB); 

    // validChildConfigurations.front()->display() works correctly here 

    return validChildConfigurations; 

} 
... 

如上註釋所示,多態性仍然正確地在這一點上,因爲工作專門的顯示功能仍在被擊中。但是,在最後的代碼片段中(如下所示),一切都崩潰了。這是從User.cpp User類:

... 
void User::doStuff() { 

    BaseRuleSet * baseRuleSet; 
    SpecialRuleSet specialRuleSet; 

    baseRuleSet = &specialRuleSet; 


    BaseConfig * currentConfig; 

    /* 
    SpecialConfig specialConfig; 

    currentConfig = &specialConfig; 

    currentConfig->display(); // this works 
    */ 


    queue<BaseConfig *> childConfigurations = ruleSet->getValidChildConfigurations(currentConfig); 

    childConfigurations.front()->display(); // this does not work 


} 

作爲最後一個註釋示出了在上面的例子中,在最後一次調用到顯示()實際上嘗試使用純虛函數在BaseConfig代替實施專門版本在SpecialConfig中。

我的想法是在C++中存在一種限制或不同的方式,我不知道或者在實現中存在錯誤。任何人都可以幫我澄清這一點嗎?

謝謝。

+1

getValidChildConfigurations返回指向函數完成後不存在的對象的指針。 – bmm6o 2012-01-16 19:30:10

回答

7

該問題與多態性無關。你的實際問題如下。

BaseConfig * baseConfigA; // Okay. 

SpecialConfig specialConfigA; // Fair enough 

baseConfigA = &specialConfigA; // Hmm, getting the address of a local variable? 

validChildConfigurations.push(baseConfigA); // This should be okay as long as 
              // you don't return that queue... 
return validChildConfigurations; // Oh dear. 

在C++中,您會看到一個局部變量與其作用域一樣長。上面的specialConfigA對象將在getValidChildConfigurations返回時立即銷燬,之後您存儲在隊列中的指針指向...未定義的東西。所以當你嘗試通過它調用一個方法時,你會遇到未定義的行爲,這對你的情況來說是一個崩潰。

解決方案是將動態分配對象SpecialConfig

BaseConfig * baseConfigA = new SpecialConfig; 

這意味着該對象在調用delete將僅被破壞。這既是一件好事,也是一件壞事:它不再超出範圍,但是當你完成時,你不能忘記使用delete,否則內存將會泄漏。解決方案是使用智能指針爲您執行delete。爲此,C++ 11擁有std::shared_ptr類。如果你仍然停留在C++ 03中,你可以使用boost::shared_ptr

如果您不能或不想使用智能指針,那麼請記住,queue的構造並不在其內容援引delete,所以你會在一個點上,並delete必須通過它循環一切。

+0

謝謝你這樣一個清楚而有用的答案。 – 2012-01-16 19:40:31

3
SpecialConfig specialConfigA; 
SpecialConfig specialConfigB; 

baseConfigA = &specialConfigA; 
baseConfigB = &specialConfigB; 

您指向曾經在堆棧上的對象。這就是它崩潰的原因 - 對象不再存在。