簡體   English   中英

C++中如何實現指向成員函數的指針?

[英]How is pointer to member function implemented in C++?

c++中指向成員函數的指針分為三部分:

Offset
Address/index
virtual?

偏移量用於在使用base pointer.

這個偏移量是如何實現的? 它是指向某個表的指針,每個派生類都有一個表並且該表包含形式為(base X, offset)的條目嗎?

另外,我在哪里可以獲得有關此的更多信息?

首先,您應該注意到 C++ 方法可以像常規函數一樣實現(並且通常實現),它在所有其他參數之前接受一個額外的隱藏參數,名為this

換句話說在

struct P2d {
   double x, y;
   void doIt(int a, double b) {
       ...
   }
};

doIt的機器代碼與 C 編譯器為

void P2d$vid$doIt(P2d *this, int a, double b) {
    ...
}

p->doIt(10, 3.14)這樣的調用被編譯為P2d$vid$doIt(p, 10, 3.14);

鑒於此,一個沒有虛方法的簡單類的方法指針可以作為指向方法代碼的常規指針來實現(注意:我將vid用於“Void of Int+Double”作為“name mangling”的玩具示例“C++ 編譯器處理重載的方式——具有相同名稱但參數不同的不同函數)。

但是,如果該類具有虛方法,則情況不再如此。

大多數 C++ 編譯器在不使用 VMT 的情況下實現虛擬調度...即

struct P2d {
    ...
    virtual void doIt(int a, double b);
};

調用p->doIt(10, 3.14)的代碼,其中pP2d *與 C 編譯器生成的代碼相同

(p->$VMTab.vid$doIt)(p, 10, 3.14);

即實例包含一個指向虛擬方法表的隱藏指針,每個成員都包含有效代碼地址(假設編譯器無法推斷p的類確實是P2d而不是派生的,因為在這種情況下調用可以是相同的至於非虛擬方法)。

方法指針需要遵守虛擬方法...即,在從P2d派生的實例上使用方法指針間接調用doIt需要調用派生版本,而在P2d實例上使用時,需要使用相同的方法指針來調用基礎版本。 這意味着選擇調用哪個代碼取決於指針和類實例。

一種可能的實現方式是使用蹦床:

void MethodPointerCallerForP2dDoit(P2d *p, int a, double b) {
    p->doIt(a, b);
}

在這種情況下,方法指針仍然只是指向代碼的指針(但指向蹦床,而不是最終方法)。

另一種方法是將方法的索引存儲為方法指針,而不是在 VMT 中。 這是可行的,因為在 C++ 中,方法指針綁定到特定的類,因此編譯器知道該類是否有虛方法。

多重繼承不會使方法指針復雜化,因為所有內容都可以在編譯時解析為單個最終 VMT 表。

這是關於 6502 的回答的評論,但我缺乏聲譽。

沒有虛方法的簡單類的方法指針可以實現為指向該方法的常規指針

我認為這種說法是不正確的,因為多重繼承會使事情復雜化。 考慮這段代碼:

struct A {
    int a;
    void f() {
        // use a
    }
};

struct B {
    int b;
    void g() {
        // use b
    }
};

// C objects may look like this in memory:
//
//     |-----|
//     |  A  |
//     |-----|
//     |  B  |
//     |-----|
//
// Since only one of A and B can be at the start of C in terms of memory
// layout, at least one of the following can't work:
//
// * naively interpreting a pointer to C as a pointer to A
// * naively interpreting a pointer to C as a pointer to B
struct C : A, B {};

void call_ptm(void (C::*ptm)()) {
    C c;
    // `ptm` could be `A::f` or `B::g`.  We don't know.  At what offset
    // relative to `this` do we expect to find data members then?  It depends
    // on whether `ptm` points to `A::f` or `B::g`, which isn't known at
    // compile time.
    (c.*ptm)();
}

不管怎樣,指向成員函數的指針需要存儲一個偏移量,該偏移量可以在調用時應用於this

暫無
暫無

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

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