C++學習 - 虛表,虛函數,虛函數表指標學習筆記

來源:互聯網
上載者:User

標籤:c++   虛函數   虛表   虛指標   

虛函數

虛函數就是用virtual來修飾的函數。虛函數是實現C++多態的基礎。

虛表

每個類都會為自己類的虛函數建立一個表,來存放類內部的虛函數成員。

虛函數表指標

每個類在建構函式裡面進行虛表和虛表指標的初始化。

下面看一段代碼:

////  main.cpp//  VirtualTable////  Created by Alps on 15/4/14.//  Copyright (c) 2015年 chen. All rights reserved.//#include <iostream>using namespace std;class Base{public:    virtual void func(){        printf("Base\n");    }    virtual void hunc(){        printf("HBase\n");    }private:    virtual void gunc(){        printf("Base Private\n");    }};class Derive: public Base{public:    virtual void func(){        printf("Derive\n");    }};class DeriveSecond: public Base{public:    void func(){        printf("Second!\n");    }};class DeriveThird: public Base{};class DeriveForth: public Base{public:    void gunc(){        printf("Derive Forth\n");    }};int main(int argc, const char * argv[]) {    Derive d;    Base *pb = &d;    pb->func();    // 1  輸出:Derive    DeriveSecond sec;    pb = &sec;    pb->func();    // 2 輸出:Derive Second    DeriveThird thi;    pb = &thi;    pb->func();    //3 輸出:Base    DeriveForth forth;    pb = &forth;//    pb->gunc();    // 4 報錯    return 0;}

在這個裡面我建立了一個基類Base還有其他衍生類別。

  • 首先// 1部分,表示了雖然我們聲明的是一個Base類的指標,但是指向的是衍生類別的執行個體,所以調用的就是衍生類別的函數。

  • 其次// 2部分,表示的和1差不多,只不過在// 2裡不是虛函數了,覆蓋了父類的虛函數。但還是存放在衍生類別的虛表裡。

  • // 3的代碼裡可以看到,衍生類別沒有覆蓋父類的虛函數的時候,雖然指向的是衍生類別的執行個體,但是調用的是父類的方法,是因為在繼承時候,子類也有一個虛表,裡面存放了父類的虛函數表。

  • // 4裡是私人的虛函數是不能直接被外部調用的。

虛表詳解

先看如下代碼:代碼來源:RednaxelaFX,程式設計語言廚此人我覺得很厲害,這裡借用一下他的代碼,無任何商用,如果有問題,請聯絡我刪除。

#include <string>#include <iostream>class Object {  int identity_hash_;public:  Object(): identity_hash_(std::rand()) { }  int IdentityHashCode() const     { return identity_hash_; }  virtual int HashCode()           { return IdentityHashCode(); }  virtual bool Equals(Object* rhs) { return this == rhs; }  virtual std::string ToString()   { return "Object"; }};class MyObject : public Object {  int dummy_;public:  int HashCode() override           { return 0; }  std::string ToString() override   { return "MyObject"; }};int main() {  Object o1;  MyObject o2;  std::cout << o2.ToString() << std::endl            << o2.IdentityHashCode() << std::endl            << o2.HashCode() << std::endl;}/*              Object                      vtable                               -16 [ offset to top     ]  __si_class_type_info                               -8  [ typeinfo Object   ] --> +0 [ ... ]--> +0  [ vptr           ] --> +0  [ &Object::HashCode ]    +8  [ identity_hash_ ]     +8  [ &Object::Equals   ]    +12 [ (padding)      ]     +16 [ &Object::ToString ]             MyObject                     vtable                               -16 [ offset to top       ]  __si_class_type_info                               -8  [ typeinfo MyObject   ] --> +0 [ ... ]--> +0  [ vptr           ] --> +0  [ &MyObject::HashCode ]    +8  [ identity_hash_ ]     +8  [ &Object::Equals     ]    +12 [ dummy_         ]     +16 [ &MyObject::ToString ]*/

這裡最主要的是我認為R大的這個虛表畫的實在是好看。所以直接借用了,一看就比我上面自己寫的代碼好看多了(T T)。

首先我們學習的時候,可以暫時先無視小於0的虛表內容。從+0開始存放了vptr這個虛表指標指向了類的虛表。可以很清楚的看到在MyObject的虛表裡其中HashCode 和 ToString函數已經是衍生類別的虛函數了,把父類的函數重寫了。

所以這兩個R大畫的類已經很清楚的說明了類的虛表虛函數的操作。

那麼有沒有比較暴力的辦法強行自己來控制虛表呢。其實這個來源於當時我做的一個阿里筆試題,做完當天我就看到知乎的R大已經做了詳細的解釋,這裡還是引用他的代碼好了。

虛表和虛函數地址

以下代碼同出自R大之手:RednaxelaFX,程式設計語言廚

#include <iostream>using namespace std;class animal{protected:  int age_;  animal(int age): age_(age) { }public:  virtual void print_age(void) = 0;  virtual void print_kind() = 0;  virtual void print_status() = 0;};class dog : public animal{public:  dog(): animal(2) { }  ~dog() { }  virtual void print_age(void) {    cout << "Woof, my age = " << age_ << endl;  }  virtual void print_kind() {    cout << "I‘m a dog" << endl;  }  virtual void print_status() {    cout << "I‘m barking" << endl;  }};class cat : public animal{public:  cat(): animal(1) { }  ~cat() { }  virtual void print_age(void) {    cout << "Meow, my age = " << age_ << endl;  }  virtual void print_kind() {    cout << "I‘m a cat" << endl;  }  virtual void print_status() {    cout << "I‘m sleeping" << endl;  }};void print_random_message(void* something) {  cout << "I‘m crazy" << endl;}int main(void){  cat kitty;  dog puppy;  animal* pa = &kitty;  intptr_t* cat_vptr = *((intptr_t**)(&kitty));  intptr_t* dog_vptr = *((intptr_t**)(&puppy));  intptr_t fake_vtable[] = {    dog_vptr[0],         // for dog::print_age    cat_vptr[1],         // for cat::print_kind    (intptr_t) print_random_message  };  *((intptr_t**) pa) = fake_vtable;  pa->print_age();    // Woof, my age = 1  pa->print_kind();   // I‘m a cat  pa->print_status(); // I‘m crazy  return 0;}

我們可以看到R大幹了什麼!!喪心病狂的把vtable自己偽造了一個,然後放到虛表指標後面!簡直佩服。看到這個代碼我也是才明白,虛表可以這麼操作。

虛表地址和虛函數地址

虛函數表的地址(int*)&classname)與虛函數的地址(int*)*(int*)(&classname)實際按照R大的說法,這裡的int應該改成intptr_t才更好,這樣能夠防止在LP64模型下,函數指標是8個位元組。而地址擷取不全。

虛函數表的地址和虛函數地址的關係類似於: x 和 *x的關係。

C++學習 - 虛表,虛函數,虛函數表指標學習筆記

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.