2016-05-15 60 views
1

我在查詢之前已經搜索過,但沒有找到任何適用於我的問題的東西。C++ - 在函數中使超類匹配子類參數的指針

我想創建一個指向超類的指針(它總是指一個子類)與一個函數中的子類參數(指針或const引用)匹配。

上下文:在C++中創建一個「高級」計算器。

讓我給你更多的細節類的我這個問題,正在使用:

首先,我們有字面:

class Literal {}; 
class IntegerLiteral : public Literal {}; 
class RealLiteral : public Literal {}; 
class RationalLiteral : public Literal {}; 
//.... 

我們必須使用存儲保存文字對象堆棧的不會忽略

// If st is instance of stack then : 
st.top(); // returns a Literal* 

而且我們有將開拆與堆棧交互操作對象文字*的正確的數字(取決於運營商的元數),在Literal *對象上應用運算符並最終堆棧結果。

class Operator { 
    int n; // operator arity 
public: 
    virtual void executeOperator(stack *st) = 0; // 
}; 

一個操作員的子類(例如)的:

class PlusOperator : public Operator { 
public: 
    virtual void execute(StackUTComputer *st) override { 
     Literal* arg1 = st->top(); 
     Literal* arg2 = st->top(); 
     Literal* result = applyOperator(arg1, arg2); 
     st->pop(); st->pop(); 
     st->push(result); 
    } 

    Literal* execute(IntegerLiteral* a, IntegerLiteral* b) { 
     return new IntegerLiteral(a->getValue() + b->getValue()); 
    } 

    Literal* execute(IntegerLiteral* a, RealLiteral* b) { 
     return new RealLiteral(a->getValue() + b->getValue()); 
    } 


    Literal* execute(IntegerLiteral* a, RationalLiteral* b) { 
     return new RationalLiteral(
      a->getValue() + (a->getValue()*b->getDenominator()), 
      b->getDenominator() 
    ); 
    } 

    // ... 
}; 

我的目的(通過重載函數applyOperator)是「神奇」讓電腦知道哪些函數調用取決於實際操作符解開的文字類型(類Literal是抽象的:堆棧將始終包含Literal的子類)。

但它不工作,我想要的方式。 我的意思是電話applyOperator(arg1, arg2)arg1arg2是Literal *)無效,因爲沒有功能與簽名匹配。我知道我使用C++多態性詮釋了它通常使用的另一種方式(即給出一個超類參數的函數的子類參數)。

我不知道如何扭轉我的架構,以便正確使用多態性,也許有一些語法有用的解決方案,以使我的想法工作。

無論哪種方式,我感謝您的幫助!

拉斐爾。

+2

散文較少,更具體的錯誤消息和[MCVE]將大大有助於改善此問題。 –

+0

在這裏的任何地方都沒有聲明'applyOperator'。 –

回答

1

有一種方法與多態性做到這一點的預期(不dynamic_cast):

#include <iostream> 
#include <memory> 
#include <string> 

struct IntegerLiteral; 
struct RealLiteral; 

struct Literal { 
    virtual void add(const Literal &) = 0; 
    virtual void add(const IntegerLiteral &) = 0; 
    virtual void add(const RealLiteral &) = 0; 

    virtual void add_to(Literal &) const = 0; 
    virtual void add_to(IntegerLiteral &) const = 0; 
    virtual void add_to(RealLiteral &) const = 0; 
    virtual std::ostream &print(std::ostream &os) const = 0; 
    virtual ~Literal() = default; 
}; 

std::ostream &operator<<(std::ostream &os, const Literal &l) { 
    return l.print(os); 
} 

struct IntegerLiteral : Literal { 
    IntegerLiteral(int i) 
     : i(i) {} 
    int i = 0; 
    void add(const Literal &other) override { 
     //now we know one operand is an IntegerLiteral and can pass on that information to the other Literal 
     other.add_to(*this); 
    } 
    void add(const IntegerLiteral &other) override { 
     i += other.i; 
    } 
    void add(const RealLiteral &other) override; 
    void add_to(Literal &other) const override { 
     other.add(*this); 
    } 
    void add_to(IntegerLiteral &other) const override { 
     other.i += i; 
    } 
    void add_to(RealLiteral &other) const override; 
    std::ostream &print(std::ostream &os) const override { 
     return os << i; 
    } 
}; 

struct RealLiteral : Literal { 
    RealLiteral(double d) 
     : d(d) {} 
    double d = 0; 
    void add(const Literal &other) override { 
     other.add_to(*this); 
    } 
    void add(const IntegerLiteral &other) override { 
     d += other.i; 
    } 
    void add(const RealLiteral &other) override { 
     d += other.d; 
    } 
    void add_to(Literal &other) const override { 
     other.add(*this); 
    } 
    void add_to(IntegerLiteral &other) const override { 
     //now we know both operands and can do the calculation 
     other.i += d; 
    } 
    void add_to(RealLiteral &other) const override { 
     other.d += d; 
    } 
    std::ostream &print(std::ostream &os) const override { 
     return os << d; 
    } 
}; 

void IntegerLiteral::add(const RealLiteral &other) { 
    i += other.d; 
} 

void IntegerLiteral::add_to(RealLiteral &other) const { 
    other.d += i; 
} 

int main() { 
    std::unique_ptr<Literal> l1 = std::make_unique<RealLiteral>(3.14); 
    std::unique_ptr<Literal> l2 = std::make_unique<IntegerLiteral>(42); 
    l1->add(*l2); 
    std::cout << *l1 << '\n'; 
} 

DEMO

你需要一噸的代碼,使這項工作,它得到每Literal二次惡化你添加和每個運營商兩次壞。另外,如果您忘記了override函數,則運行時可能會出現無限循環和堆棧溢出。
一個更好的方法(更容易編寫和運行更快)將只使用doubleBigNum的一切,而不會打擾多態。

+0

感謝您花時間撰寫本演示。我現在看到了我試圖實施的糟糕架構。我會改變它,以便簡單地使用'雙'的一切。 – raphael

0

您正在混合不同的概念,它們是ad hoc polymorphism(過載)和subtype polymorphism,它們通過虛擬表的方法的後期綁定來實現。

基本上會發生的事情是,編譯器會選擇在編譯時調用哪個重載方法,而不是在運行時調用。這使得你在不使用RTTI時試圖做的事情變得不可能。

編譯器無法確定哪個將是兩個Literal實例的類型,並且C++在處理非虛擬方法時僅支持早期綁定。它在編譯時唯一可以推導出的事實是,兩個參數都是Literal*類型,因此它僅查找過載。

你需要一種動態開關來做你想做的事情,這可以通過使用dynamic_cast(或類似的手工製作的解決方案)來獲得。

+0

謝謝你的解釋。我清楚地看到現在爲什麼編譯器無法確定要做什麼。 – raphael