0

我瞭解C++類的多態性的方式,它允許以相同的方式處理類及其子類。因此,如果我有一個類及其子類的多個對象,並將它們存儲在基類的(智能)指針向量中,則可以調用它們上的任何虛擬方法,它將工作得很好。如何在處理基類的多重指針時同時處理多態性?

class Dancer { 
public: 
    virtual void f() const { std::cout << "I am a basic dancer" << std::endl; } 
}; 

class SkilledDancer : public Dancer { 
public: 
    void f() const { std::cout << "I am a skilled dancer" << std::endl; } 
}; 

int main(int argc, char *argv[]) 
{ 
    std::vector< std::shared_ptr<Dancer> > dancers; 
    dancers.push_back(std::make_shared<Dancer>(Dancer())); 
    dancers.push_back(std::dynamic_pointer_cast<Dancer>(std::make_shared<SkilledDancer>(SkilledDancer()))); 

    for(auto & dancer : dancers){ 
     dancer->f(); //works fine 
    } 
} 

但現在我有一個問題,當我想這種行爲,使用運營商或與兩個對象處理方法時。如果一個函數需要輸入基類的兩個參數,我該如何考慮它們實際上可能是派生類的對象的事實?

class Dancer { 
public: 
    virtual void g(const Dancer & d) const { std::cout << "Let's do a basic dance" << std::endl; } 
}; 

class SkilledDancer : public Dancer { 
public: 

    //what should I do here ? 

    void g(const Dancer & d) const { std::cout << "Let's do an advanced dance" << std::endl; } 
    //would overload Dancer::g but wrong because d is only a Dancer 

    void g(const SkilledDancer & d) { std::cout << "Let's do an advanced dance" << std::endl; } 
    //doest not overload Dancer::g because different signature 
    //so would never be called if dealing with two smart pointers of the base class 
}; 

int main(int argc, char *argv[]) 
{ 
    auto basic = std::make_shared<Dancer>(Dancer()); 
    auto advanced = std::dynamic_pointer_cast<Dancer>(std::make_shared<SkilledDancer>(SkilledDancer())); 
    basic->g(*advanced); //OK 
    advanced->g(*advanced); //seems good ... 
    advanced->g(*basic); //... but wrong 
} 

我設法找到一個解決方法(下面的代碼),但它需要派生類的一個其他成員,另外兩個函數調用,該函數不能是const了。所以我想知道是否有更好的方法來處理這個問題。

如果我面臨XY問題,我的實際問題是關於矩陣。我希望有一個矩陣算子乘以矩陣,但是當處理兩個特定的矩陣(如三角形或對稱矩陣類的子類)時,我想調用另一個算子。請注意,所有類型的矩陣都存儲爲矩陣基類的指針。

class Dancer { 
public: 
    virtual void g(const std::shared_ptr<Dancer> & d) { std::cout << "Let's do a basic dance" << std::endl; } 
}; 

class SkilledDancer : public Dancer { 
public: 
    SkilledDancer() : dummy_g(false) {} 
    bool dummy_g; 

    void g(const std::shared_ptr<Dancer> & d) { 
     if(!dummy_g){ 
      dummy_g = true; 
      d->g(std::dynamic_pointer_cast<Dancer>(std::make_shared<SkilledDancer>(*this))); 
     } else { 
      dummy_g = false; 
      ((SkilledDancer*)d.get())->dummy_g = false; 
      std::cout << "Let's do an advanced dance" << std::endl; 
     }  
    } 
}; 

int main(int argc, char *argv[]) 
{ 
    //all cases work fine 
    basic->g(basic); 
    advanced->g(advanced); 
    basic->g(advanced); 
    advanced->g(basic); 
} 
+2

查找雙倍/多次調度。 –

+1

[OT]:您應該從'std :: make_shared (Dancer())'' – Jarod42

+0

'中移除'Dancer()'謝謝你告訴我背後的概念的名字。因此,如果我理解正確,那麼在C++中就不存在對多次調度的本地支持(維基百科文章提到訪客模式的動態轉換,但它們似乎不是很好的選擇)。 好評的make_shared評論! – yultan

回答

1

這就是你剛剛試圖用你的「舞者」的例子嗎?

class Dancer 
{ 
public: 
    virtual void danceWith(Dancer * dancer) { 
     std::cout << "Let's do a basic dance" << std::endl; 
    } 
}; 

class SkilledDancer : public Dancer 
{ 
public: 
    void danceWith(Dancer * dancer) override 
    { 
     if(auto skilledDancer = dynamic_cast< SkilledDancer*>(dancer)) { 
      danceWith(skilledDancer); 
     } else { 
      Dancer::danceWith(dancer); 
     } 
    } 

    void danceWith(SkilledDancer * dancer) { 
     std::cout << "Let's do an advanced dance" << std::endl; 
    } 
}; 

後來:

Dancer* basic = new Dancer; 
SkilledDancer* advanced = new SkilledDancer; 
Dancer* pretending = advanced; 

basic ->danceWith(basic);   // -> Let's do a basic dance 
advanced->danceWith(advanced);  // -> Let's do an advanced dance 
basic ->danceWith(advanced);  // -> Let's do a basic dance 
advanced->danceWith(basic);   // -> Let's do a basic dance 
advanced->danceWith(pretending); // -> Let's do an advanced dance 

基本上,我們只使用該編譯器總是選擇的函數重載在參數方面擬合大部分事實。在SkilledDancer類中:對於所有SkilledDancer*類型參數,將調用danceWith()的第二個重載(並且它在編譯時決定 - 沒有多態轉換開銷)。同時,所有Dancer*類型的參數都需要運行時檢查,並且調用覆蓋的danceWith()。執行檢查並選擇要呼叫的相應版本danceWith()。如果舞者基本 - 跳舞基本,如果他只是假裝 - 舞蹈先進。