簡體   English   中英

在C ++中,為什么編譯器在編譯時不理解基類對象指向哪個對象?

[英]in C++, why compiler does not understand which object is pointed by base class object at compile time?

在C ++中,為什么編譯器在編譯時不理解基類對象指向哪個對象?

對於前。

int Add(int nX, int nY)
{
    return nX + nY;
}
int Subtract(int nX, int nY)
{
    return nX - nY;
}

int main()
{
    // Create a function pointer and make it point to the Add function
    int (*pFcn)(int, int) = Add;
    cout << pFcn(5, 3) << endl; // add 5 + 3
    pFcn = Subtract;
    cout<< pFcn(5,3)<<endl // pefrom 5-3
    return 0;
}

上面的代碼段是后期綁定或動態綁定的示例。

在上面的示例中,只有在編譯時,我們才知道第一個Add函數將通過pFcn調用,然后Subtract函數將被調用。 那么,為什么即使編譯器只知道在編譯時要調用哪個函數,也可以將其稱為動態綁定的示例呢?


我的問題也與虛擬功能有關。 考慮跟隨前,

class Base {
public:
    void NonVirtual() {
        cout << "Base NonVirtual called.\n";
    }
    virtual void Virtual() {
        cout << "Base Virtual called.\n";
    }
};
class Derived : public Base {
public:
    void NonVirtual() {
        cout << "Derived NonVirtual called.\n";
    }
    void Virtual() {
        cout << "Derived Virtual called.\n";
    }
};
int main() {
    Base* bBase = new Base();
    Base* bDerived = new Derived();

    bBase->NonVirtual();
    bBase->Virtual();
    bDerived->NonVirtual();
    bDerived->Virtual();
}                                               

動態綁定在這里發生。 在運行時確定要調用哪個函數。 那么,為什么編譯器在編譯時不能僅決定調用哪個函數呢?

在一般情況下,使用函數指針請求后期綁定,但是如果能夠證明只有一個函數可以綁定,那么足夠聰明的編譯器可以優化該請求並進行早期綁定。 條件優化規則允許這樣

  • 該程序要求延遲綁定。
  • 但是早期綁定不會改變程序的可觀察行為。 早期綁定是合乎需要的,因為它更快,使程序更小且所需的RAM更少(可以刪除pFcn )。
  • 因此,可以改為執行早期綁定。

考慮一下,您可能會以編譯器無法進行早期綁定的方式傳遞函數指針,這可能是因為它不夠聰明,無法檢測到可能,或者是因為它與無法觀察的代碼進行交互:

using BinaryIntegerOperator = int (*)(int, int);

int Add(int a, int b) { return a + b; }
int Subtract(int a, int b) { return a - b; }

extern int accumulate(
    int initial,
    int *first,
    std::size_t count,
    BinaryIntegerOperator op
);

現在,您可以將AddSubtract作為第四個參數。 被調用的函數可能不是同一二進制文件的一部分(也許它是動態鏈接庫的一部分),因此后期綁定是不可避免的accumulate()應該已經由其他編譯器進行了編譯。

在編譯系統可以證明只有一個函數可以調用的情況下,允許但不要求刪除間接調用並直接調用該函數。 這適用於函數指針和虛擬方法分派。 通常,對於虛擬方法,優化將作為鏈接時間優化的一部分進行,鏈接器會意識到其中只有給定類的一個實例具有虛擬方法,或者可能沒有任何類覆蓋虛擬方法。

我不確定優化是否被認為非常重要,但是我希望這兩種情況都會在使用最佳編譯器的簡單情況下發生。 (第一個示例將脫離相當標准的編譯器優化技術。幾乎可以肯定的是,針對vtables進行此操作需要經過專門構建的優化過程,因為編譯器必須知道vtable在運行時是如何修改或不修改。

通常,簡化使用某種機制並不意味着不使用指定的機制。 這就是為什么即使編譯器可以證明函數指針沒有有趣的動態行為,也可以說函數指針是動態綁定的原因。

給定上面更新的代碼,請考慮以下事項:

int main(int argc, char **argv) {
    Base* bBase = new Base();
    Base* bDerived = new Derived();

    // Use test on argc to abstract arbitrary computation.
    Base *one_or_the_other = (argc > 1) ? bDerived : bBase;
    one_or_the_other->NonVirtual();
    one_or_the_other->Virtual();
}

編譯器無法再在編譯時確定要調用第二次調度的虛擬方法。

暫無
暫無

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

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