1. 程式人生 > >C++遲後聯編和虛擬函式表

C++遲後聯編和虛擬函式表

先看一個題目:

class Base
{
public:
    virtual void Show(int x)
    {
        cout << "In Base class, int x = " << x << endl;
    }
};

class Derived : public Base
{
public:
    virtual void Show(float x)
    {
        cout << "In Derived, float x = " << x << endl;
    }
};

void test (Base &b) { int i = 1; b.Show(i); float f = 2.0; b.Show(f); } int main(int argc, char *argv[]) { Base bc; Derived sc; test(bc); test(sc); return 0; } 輸出結果為:D A、In Base class, int x = 1; In Base class, int x = 2; In Derived,
int x = 1; In Derived, float x = 2; B、In Base class, int x = 1; In Base class, int x = 2; In Derived, float x = 1; In Derived, float x = 2; C、In Base class, int x = 1; In Base class, int x = 2; In Base, int x = 1; In Base, float x = 2; D、In Base class, int x = 1; In Base
class, int x = 2; In Base class, int x = 1; In Base class, int x = 2;

理由:如果虛擬函式在基類與子類中出現的僅僅是名字的相同,而引數型別不同,或者返回型別不同,即使寫上了virtual關鍵字,也不進行遲後聯編。

stackoverflow上,可以看到解釋,http://stackoverflow.com/questions/27227189/override-virtual-function-with-different-parameters-in-c

C++裡有兩種編譯型別:
1) 先期聯編或靜態聯編:在編譯時就能進行函式聯編稱為先期聯編或靜態聯編。
2) 遲後聯編或動態聯編:在執行時才能進行的聯編稱為遲後聯編或動態聯編。

virtual關鍵字的作用就是提示編譯器進行遲後聯編,告訴連結過程:“我是個虛的,先不要連線我,等執行時再說”。 具體原理:當編譯器遇到virtual後,會為所在的類構造一個表和一個指標,那個表叫做vtbl,每個類都有自己的vtbl,vtbl的作用就是儲存自己類中虛擬函式的地址,我們可以把vtbl形象地看成一個數組,這個陣列的每個元素存放的就是虛擬函式的地址,指標叫做vptr,指向那個表。而這個指標儲存在相應的物件當中,也就是說只有建立了物件以後才能找到相應虛擬函式的地址。 

對於下面這種常見程式碼(假如Base是Derive的父類):

Base *p=new Derive();
p->virtual_fun();

在程式執行時,根據物件的型別去初始化vptr,從而讓vptr正確的指向所屬類的虛表。上述程式中,由於p實際指向的物件型別是Derive,因此vptr指向的Derive類的vtable,當呼叫p->virtual_fun()時,根據虛表中的函式地址找到的就是Derive類的virtual_func()函式。

假設我們有這樣的一個類:

class Base {

     public:

            virtual void f() { cout << "Base::f" << endl; }

            virtual void g() { cout << "Base::g" << endl; }

            virtual void h() { cout << "Base::h" << endl; }

};

對應的虛擬函式表:

假設有如下所示的一個繼承關係:

對於例項:Derive d; 的虛擬函式表如下:

如果是多繼承:

對於例項:Derive d; 的虛擬函式表如下:

而C++標準規定:為確保執行時的多型定義的基類與派生類的虛擬函式不僅函式名要相同,其返回值及引數都必須相同,否則即使加上了virtual,系統也不進行遲後聯編。

因此,對於最初的題目,Base的虛擬函式表裡僅僅有一個Show方法,由於Derived子類過載了Show函式,那麼Derived的虛擬函式表裡實際上有兩個Show方法,

因此,從Base角度去呼叫Show方法也只能是呼叫Base自己的方法了。

虛擬函式表的例子參考了:http://blog.csdn.net/haoel/article/details/1948051

相關推薦

C++虛擬函式

先看一個題目: class Base { public: virtual void Show(int x) { cout << "In Base class, int x = " << x << endl; } }

【轉】C++動態繫結虛擬函式vtable (動態實現原理)

 關於C++內部如何實現多型,對程式設計師來說即使不知道也沒關係,但是如果你想加深對多型的理解,寫出優秀的程式碼,那麼這一節就具有重要的意義。 我們知道,函式呼叫實際上是執行函式體中的程式碼。函式體是記憶體中的一個程式碼段,函式名就表示該程式碼段的首地址,函式執行時就從這裡開始。說得簡單

[C/C++]C++中虛擬函式的原理虛擬函式

#include using namespace std; class A{     public:     A();     virtual void fun1();     void fun2(); }; A::A() { } void A::fun1() {     cout<<"I am

C++的靜態動態技術

聯編是指一個計算機程式自身彼此關聯的過程。按照聯編所進行的階段不同,可分為兩種不同的聯編方法:靜態聯編和動態聯編。 靜態聯編 靜態聯編是指聯編工作出現在編譯連線階段,這種聯編又稱早期聯編,因為這種聯編過程是在程式開始執行之前完成的。 在編譯時所進行的這種聯編又稱靜態束定。在編

探究C++中的成員函式指標虛擬函式

say something 相信對C++物件有一定了解的話,應該都會知道,在C++中物件的實現中,成員函式和成員變數是分離的 所以我們所談到的非靜態成員函式其實只是一個普通的函式(不過被編譯器所隱藏,必須繫結到特定的物件上才能執行) 靜態成員函式實際上就真

C++ 類的儲存方式以及虛擬函式

一、C++成員函式在記憶體中的儲存方式   用類去定義物件時,系統會為每一個物件分配儲存空間。如果一個類包括了資料和函式,要分別為資料和函式的程式碼分配儲存空間。按理說,如果用同一個類定義了10個物件,那麼就需要分別為10個物件的資料和函式程式碼分配儲存單元,如下圖所示。  

繼承、 虛繼承虛擬函式對類的大小的影響

一、真空類 class CNull { }; 長度:1 記憶體結構: ?? 評註:長度其實為0,這個位元組作為內容沒有意義,可能每次都不一樣。 二、空類 class CNull2 { public:     CNull2(){printf("Construct/

C++ 關於類與物件在虛擬函式上唯一性問題 淺析

【摘要】 很多教材上都有介紹到虛指標、虛擬函式與虛擬函式表,有的說類物件共享一個虛擬函式表,有的說,一個類物件擁有一個虛擬函式表;還有的說,無論使用者聲明瞭多少個類物件,但是,這個VTABLE虛擬函式表只有一個;也有的在說,每個具有虛擬函式的類的物件裡面都有一個VPTR虛擬

c++多型的原理 以及虛擬函式詳解

c++中多型的原理 要實現多型,必然在背後有自己的實現機制,如果不瞭解其背後的機制,就很難對其有更深的理解。 一個多型的例子 class Person{ public: virtual void Drink() { cout << "drink water" &

虛擬函式虛擬函式

虛擬函式的作用:用於實現C++的多型。 虛擬函式表:具有虛擬函式的類在編譯階段會建立一個虛擬函式表vtable。 虛擬函式指標:每個類物件有一個虛擬函式指標vptr,vptr指向vtable。 虛擬函式表是虛擬函式指標的陣列 多重繼承的虛擬函式表: 在派生類De

C++中的虛擬函式與靜態動態

          程式在呼叫函式時,將使用哪個可執行程式碼塊呢?編譯器負責回答這個問題,將原始碼中的函式呼叫解釋為執行特定的函式程式碼塊被稱為函式名聯編。在C中,因為每個函式名都對應一個不同的函式,而在C++中,由於函式過載的緣故,編譯器必須檢視函式引數以及函式名才能確定

C++虛擬函式在虛繼承繼承中的差別

下面的程式碼在gcc和VC中的結果 #include <cstdio> class A { public: virtual void funcaa() { printf("class A %s\n",__func__); } }; class AA:virtual pu

C++的中的繼承,多型虛擬函式

首先繼承,多型,虛擬函式,我們先了解一下各位的關係。 繼承是子類繼承父類,完成基礎功能的獲取,當然繼承有三種許可權,public,protect和private,如果不加許可權限定,預設繼承是私有繼承。 許可權表如下: 所以可以看到凡私有成員,子類都不能用,不過有方法能用,這裡不討

C++中多型虛擬函式怎麼回答

1.定義:       多型性可以簡單地概括為“一個介面,多種方法”,程式在執行時才決定呼叫的函式,它是面向物件程式設計領域的核心概念。多型(polymorphism),字面意思多種形狀。   C++多型性是通過虛擬函式來實現的,虛擬函式允許子類重新定義成員函式,而子類重新

北京大學MOOC C++學習筆記(五)虛擬函式多型

虛擬函式: 在類的定義中,前面有 virtual 關鍵字的成員函式就是虛擬函式。 class base { virtual int get() ; }; int base::get() { } virtual 關鍵字只用在類定義裡的函式宣告中,寫函式體時不用。 多型的表現

C++ 虛擬函式 獲取C++虛地址虛擬函式地址

學過C++的應該都對虛表有所耳聞,在此就不過多介紹概念了,通過實 例來演示一下如何獲取虛表地址和虛擬函式地址。         簡單說一下虛表的概念:在一個類中如果有虛擬函式,那麼此類的例項中就有一個虛表指標指向虛表,這個虛表是一塊兒專門存放類的虛擬函式地址的記憶體。 圖示說

C++ 第六章(多型性虛擬函式)上篇

目錄 多型性 多型性的定義 例子 虛擬函式 虛擬函式的作用 什麼情況下使用虛擬函式 虛解構函式 純虛擬函式和抽象類 什麼是多型性 面對不同的物件傳送同一個訊息,不同的物件

C++ 第六章(多型性虛擬函式)下篇(虛解構函式虛擬函式

一,虛解構函式 如果用new運算子建立了臨時物件,若基類中有解構函式並且定義了一個指向該基類的指標變數。在程式用帶指標引數的delete運算子撤銷物件時,會發生:系統只執行基類的解構函式,而不執行派生類的解構函式。 #include<iostream> using

C++學習之多型篇(虛擬函式虛解構函式的實現原理--虛擬函式

通過下面的程式碼來說明: #include <iostream> #include <stdlib.h> #include <string> using namespace std; /**  *  定義動物類:Animal  *  成員

C++程式設計(八)—— 多型性虛擬函式

一、多型性         靜態聯編所支援的多型性稱為編譯時的多型性,當呼叫過載函式時,編譯器可以根據呼叫時所使用的實參在編譯時就確定應該呼叫哪個函式;動態聯編所支援的多型性稱為執行時的多型性,這由虛擬函式來支援。虛擬函式類似於過載函式,但與過載函式的實現策略不同,即對虛擬