考慮最簡單的有虛函式的繼承關係:
class f
};class s : public f
};此時,我們可以定義乙個父類的指標,實際指向乙個子類的物件。呼叫func函式的結果是子類的函式。虛函式在這裡是動態繫結的。
f *f = new s();
f->func(); 輸出s func
我們知道子類即使不定義虛函式也會繼承該虛函式表。而且乙個類,除了繼承的虛函式表之外,自行定義的虛函式公用乙個虛函式表。即使繼承的虛函式名稱相同,但是來自於不同的父類,即繼承了多個父類相同的虛函式,依舊是繼承每個父類的虛函式表。
class f
};class m
};class s : public f ,public m
virtual void foo() {}
};在這種情況下,s一共有三個虛函式表,繼承自兩個父類的,每個父類都有乙個,以及自己定義的。 繼承父類的,兩個虛函式表中的func的指標都是指向了類s自己定義的函式。
需要注意的是,編譯的時候,非虛函式會鏈結繫結,類本身會構造虛函式表,子類和父類的虛函式表項可能指向不同的地方。
假設考慮定義的不是指標的情況。
s s;
f f = s;
f.func(); 輸出f func
f f = s;這裡應該是編譯器預設為我們生成的賦值建構函式。
f物件 s物件的虛函式表是形成了,表項中的指標也已經確定了,指向各自的函式。 賦值建構函式,我們沒有定義,預設的這裡測試的結果是什麼都沒做。無論如何,這裡賦值建構函式與虛函式表無關。
如果是指標的情況,如果是父類的指標指向子類的例項,父類的非虛函式以及和類本身繫結好了,依舊會呼叫父類的函式。但是虛函式要通過虛函式表進行訪問,那麼父類的指標就會訪問到子類的虛函式表,從而呼叫子類的函式。
反過來,如果讓子類的指標指向父類的物件,首先會編譯錯誤,如果強制型別轉換(都是指標嘛),你可能會看到段錯誤。因為子類的記憶體是大於父類的記憶體的,父類的一些資料在子類中順序可能不是你想的那樣了。。
虛函式表指標總是在類的成員變數之前。
乙個單繼承的例子:
class f
virtual void t()
char a;
f():a(1) {}
};class s : public f
//virtual void t() {}
//virtual void m() {}
char b;
s():b(2) {}
};class g : public s
virtual void func2()
g():c(3) {}
double c;
};class l
};int main()
vtableaddr = (int *)((char *)vtableaddr + 4);
printf("a = %d\n",*((char *)vtableaddr));
vtableaddr = (int *)((char *)vtableaddr + 1);
printf("b = %d\n",*((char *)vtableaddr));
vtableaddr = (int *)((char *)vtableaddr + 3);
printf("c = %f\n",*((double *)vtableaddr));
return 0;我們可以看到乙個類的記憶體布局:首先是虛函式表指標,然後是父類的物件,然後是子類的物件。這裡輸出子類的物件的偏移計算時,需要考慮位元組對齊。由於使用的gcc編譯器,預設對齊係數是4.所以偏移計算如上。
C 虛函式表
一般來說,對於開發者我們只需要知道虛函式的使用方法,以及虛函式表的存在即可。但面試時往往會遇到更細節的問題,比如讓你實現乙個虛函式機制,雖然不太實用,總歸了解些底層知識也是件好事。但如果有人苦苦相逼一定要拿這個刷人,你就去罵他吧,你才是寫編譯器的,你們全家都是寫編譯器的。唉,我有些失態了.1.虛函式...
C 虛函式表
c 中的虛函式的作用主要是實現了多型的機制。關於多型,簡而言之就是用父型別別的指標指向其子類的例項,然後通過父類的指標呼叫實際子類的成員函式。這種技術可以讓父類的指標有 多種形態 這是一種泛型技術。所謂泛型技術,說白了就是試圖使用不變的 來實現可變的演算法。比如 模板技術,rtti技術,虛函式技術,...
C 虛函式表
解析c 的虛函式表 c 中的虛函式的作用主要是實現了多型的機制。關於多型,簡而言之就是用父型別別的指標指向其子類的例項,然後通過父類的指標呼叫實際子類的成員函式。這種技術可以讓父類的指標有 多種形態 這是一種泛型技術。所謂泛型技術,說白了就是試圖使用不變的 來實現可變的演算法。比如 模板技術,rtt...