簡體   English   中英

編譯器如何知道在這種情況下調用哪個虛擬 function?

[英]How does the compiler know which virtual function to call in this situation?

我正在為考試而學習,我得到了這個代碼:

#include <iostream>
#include <string>
#include <cmath>

using namespace std;

class Expression {
public:
    Expression() = default;
    Expression(const Expression&) = delete;
    Expression& operator=(const Expression&) = delete;
    virtual ~Expression() {}
    virtual double eval()const = 0;
    virtual void print(ostream& out)const = 0;
    friend ostream& operator<<(ostream& out, const Expression& e) {
        // cout << "@";
        e.print(out);
        return out;
    }
};

class BinaryExpression : public Expression {
    Expression* _e1, * _e2;
    char _sign;
    virtual double eval(double d1, double d2)const = 0;
public:
    BinaryExpression(Expression* e1, Expression* e2, char sign) : _e1(e1), _e2(e2), _sign(sign) {}
    ~BinaryExpression() override { delete _e1; delete _e2; }
    virtual double eval()const override {
        cout << "BE eval" << endl;
        return eval(_e1->eval(), _e2->eval());
    }
    virtual void print(ostream& out)const override {
        out << '(' << *_e1 << _sign << *_e2 << ')';
    }
};

class Sum : public BinaryExpression {
    virtual double eval(double d1, double d2)const override {
        cout << "Sum private eval" << endl;
        return d1 + d2;
    }
public:
    Sum(Expression* e1, Expression* e2) : BinaryExpression(e1, e2, '+') {}
};

class Exponent : public BinaryExpression {
    virtual double eval(double d1, double d2)const override {
        cout << "E private eval" << endl;
        return std::pow(d1, d2);
    }
public:
    Exponent(Expression* e1, Expression* e2) : BinaryExpression(e1, e2, '^') {}
};

class Number : public Expression {
    double _d;
public:
    Number(double d) : _d(d) {}
    virtual double eval()const override {
        cout << "Num eval" << endl;
        return _d;
    }
    virtual void print(ostream& out)const override {
        out << _d;
    }
};

int main() {
    Expression* e = new Sum(
        new Exponent(
            new Number(2),
            new Number(3)),
        new Number(-2));

    cout << *e << " = " << e->eval() << endl;
    delete e;
}

我使用調試器查看執行了哪些行,但我仍然想知道編譯器如何知道每次在我們調用e->eval()main()中調用哪個 function

Output:

BE eval
Num eval
BE eval
Num eval
Num eval
E private eval
Sum private eval
((2^3)+-2) = 6

鑒於每個 class 都有一個 eval function 其中一些甚至有 2 並且使用表達式指針讓我有點失望。 編譯器在尋找每次運行哪個eval()時究竟搜索什么?

您的問題有兩個主題:

鑒於某些類具有多個同名函數,編譯器如何知道使用哪個 function?

嗯,他們可能有相同的名字,但他們沒有相同的簽名 eval()eval(x,y)調用之間沒有歧義,因為只有一個eval不接受任何 arguments,只有一個eval接受兩個 arguments。

給定Expression* e編譯器如何知道在e->eval()表達式中調用哪個 function ?

答案是編譯器不知道。 這發生在運行時,而不是編譯期間。 除非應用了一種稱為去虛擬化的高級優化技術(這是一個我不打算在這里討論的大話題)。

通常1當在 class 上定義虛函數時,您的編譯器將在該類型的每個 object 中存儲附加數據,即所謂的vtable ,它只是 function 指針的數組。 那么當你在一個虛方法上做e->eval()時,編譯器會用兩個步驟代替這個調用:(1)從存儲在e object對應eval虛方法的vtable中獲取function指針,(2)調用那個function帶有e object 的指針(以及可能的其他參數)。

1 :這是一個實現細節,可能的策略之一,不一定會發生什么。

虛函數基於VTable工作。

簡而言之,在聲明 class 時,它實際上需要 class 具有指向實際函數的指針數組。 它不是定期調用 function,而是首先從數組中獲取該地址,然后調用它。

在實踐中,虛擬類在 class 中有 1 個額外的指針,該指針指向該數組(VTable),並且當您的 class 被構造時,該指針被填充為該 ZA2F2ED4F8EBC2CBB4C21A29DZ40AB61 的 VTable 的地址(在編譯時計算)。

所以簡單的心理 model:它是指向函數而不是指向數據的指針,背后有一些編譯器魔法。

如果你真的想要,你可以在 C 中使用 function 指針和大量手動代碼來模擬這個。 了解它們肯定是一個很好的練習。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM