程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C++中的多態與虛函數的外部完成辦法

C++中的多態與虛函數的外部完成辦法

編輯:關於C++

C++中的多態與虛函數的外部完成辦法。本站提示廣大學習愛好者:(C++中的多態與虛函數的外部完成辦法)文章只能為提供參考,不一定能成為您想要的結果。以下是C++中的多態與虛函數的外部完成辦法正文


1、什麼是多態

多態性可以復雜概括為“一個接口,多種行為”。

也就是說,向不同的對象發送同一個音訊, 不同的對象在接納時會發生不同的行為(即辦法)。也就是說,每個對象可以用自己的方式去呼應共同的音訊。所謂音訊,就是調用函數,不同的行為就是指不同的完成,即執行不同的函數。這是一種泛型技術,即用相反的代碼完成不同的舉措。這表現了面向對象編程的優越性。

多態分為兩種:

(1)編譯時多態:次要經過函數的重載和模板來完成。

(2)運轉時多態:次要經過虛函數來完成。

2、幾個相關概念

(1)掩蓋、重寫(override)

override指基類的某個成員函數為虛函數,派生類又定義一成員函數,除函數體的其他局部都與基類的成員函數相反。留意,假如只是函數名相反,形參或前往類型不同的話,就不能稱為override,而是hide。

(2)重載(overload)

指同一個作用域出生多個函數名相反,但是形參不同的函數。編譯器在編譯的時分,經過實參的個數和類型,選擇最終調用的函數。

(3)隱藏(hide)

分為兩種:

1)部分變量或許函數隱藏了全局變量或許函數
2)派生類擁有和基類同名的成員函數或成員變量。

發生的後果:使全局或基類的變量、函數不可見。

3、幾個復雜的例子

/****************************************************************************************************** 
* File:PolymorphismTest 
* Introduction:測試多態的一些特性。 
* Author:CoderCong
* Date:20141114 
* LastModifiedDate:20160113 
*******************************************************************************************************/ 
#include "stdafx.h" 
#include <iostream> 
using namespace std; 
class A 
{ 
public: 
  void foo() 
  { 
    printf("1\n"); 
  } 
  virtual void fun() 
  { 
    printf("2\n"); 
  } 
}; 
class B : public A 
{ 
public: 
  void foo() //由於基類的foo函數並不是虛函數,所以是隱藏,而不是重寫 
  { 
    printf("3\n"); 
  } 
  void fun() //重寫 
  { 
    printf("4\n"); 
  } 
}; 
int main(void) 
{ 
  A a; 
  B b; 
  A *p = &a; 
  p->foo(); //輸入1。 
  p->fun(); //輸入2。 
  p = &b; 
  p->foo(); //輸入1。由於p是基類指針,p->foo指向一個具有固定偏移量的函數。也就是基類函數 
  p->fun(); //輸入4。多態。雖然p是基類指針,但實踐上指向的是一個子類對象。p->fun指向的是一個虛函數。依照靜態類型,調用子類函數   
  return 0; 
}

4、運轉時多態以及虛函數的外部完成

看了上邊幾個復雜的例子,我豁然開朗,原來這就是多態,這麼復雜,明白啦!

好,那我們再看一個例子:

class A 
{ 
public: 
  virtual void FunA() 
  { 
    cout << "FunA1" << endl; 
  }; 
  virtual void FunAA() 
  { 
    cout << "FunA2" << endl; 
  } 
}; 
class B 
{ 
public: 
  virtual void FunB() 
  { 
    cout << "FunB" << endl; 
  } 
}; 
class C :public A, public B 
{ 
public: 
  virtual void FunA() 
  { 
    cout << "FunA1C" << endl; 
  }; 
}; 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
  C objC; 
  A *pA = &objC; 
  B *pB = &objC; 
  C *pC = &objC; 
 
  printf("%d %d\n", &objC, objC); 
  printf("%d %d\n", pA, *pA); 
  printf("%d %d\n", pB, *pB); 
  printf("%d %d\n", pC, *pC); 
 
  return 0; 
}

運轉後果:

5241376 1563032

5241376 1563032

5241380 1563256

5241376 1563032

細心的同志一定發現了pB出了問題,為什麼明明都是指向objC的指針,pB跟他人的值都不一樣呢?

是不是編譯器出了問題呢?

當然不是!我們先講結論:

(1)每一個含有虛函數的類,都會生成虛表(virtual table)。這個表,記載了對象的靜態類型,決議了執行此對象的虛成員函數的時分,真正執行的那一個成員函數。

(2)關於有多個基類的類對象,會有多個虛表,每一個基類對應一個虛表,同時,虛表的順序和承繼時的順序相反。

(3)在每一個類對象所占用的內存中,虛指針位於最前邊,每個虛指針指向對應的虛表。

先從復雜的單個基類說起:

class A 
{ 
public: 
  virtual void FunA() 
  { 
    cout << "FunA1" << endl; 
  } 
  virtual void FunA2() 
  { 
    cout << "FunA2" << endl; 
  } 
}; 
 
class C :public A 
{ 
  virtual void FunA() 
  { 
    cout << "FunA1C" << endl; 
  }
}; 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
  A *pA = new A; 
  C *pC = new C; 
  typedef void (*Fun)(void); 
 
  Fun fun= (Fun)*((int*)(*(int*)pA)); 
  fun();//pA指向的第一個函數 
  fun = (Fun)*((int*)(*(int*)pA) +1); 
  fun();//pA指向的第二個函數 
   
  fun = (Fun)*((int*)(*(int*)pC)); 
  fun();//pC指向的第一個函數 
  fun = (Fun)*((int*)(*(int*)pC) + 1); 
  fun();//pC指向的第二個函數 
  return 0; 
}

運轉後果:

FunA1
FunA2
FunA1C
FunA2 是不是有點暈?沒關系。我一點一點解釋:pA對應一個A的對象,我們可以畫出這樣的一個表:        這就是對象*pA的虛表,兩個虛函數以聲明順序陳列。pA指向對象*pA,則*(int*)pA指向此虛擬表,則(Fun)*((int*)(*(int*)pA))指向FunA,同理,(Fun)*((int*)(*(int*)pA) + 1)指向FunA2。所以,呈現了前兩個後果。 依據後兩個後果, 我們可以揣測*pC的虛表如下圖所示:        也就是說,由於C中的FunA重寫(override)了A中的FunA,虛擬表中虛擬函數的地址也被重寫了。 就是這樣,這就是多態完成的外部機制。 我們再回到最初的問題:為什麼*pB出了問題。 依據上邊的結論,我們大膽地停止猜想:由於C是由A、B派生而來,所以objC有兩個虛擬表,而由於表的順序,pA、pC都指向了對應於A的虛擬表,而pB則指向了對應於B的虛擬表。做個實驗來驗證我們的猜測能否正確: 我們不改動A、B、C類,將問題中的main改一下:
int _tmain(int argc, _TCHAR* argv[]) 
{ 
  C objC; 
  A *pA = &objA; 
  B *pB = &objC; 
  C *pC = &objC; 
   
  typedef void (*Fun)(void); 
 
  Fun fun = (Fun)*((int*)(*(int*)pC)); 
  fun();//第一個表第一個函數 
  fun = (Fun)*((int*)(*(int*)pC)+1); 
  fun();//第一個表第二個函數 
  fun = (Fun)*((int*)(*((int*)pC+1))); 
  fun();<span > </span>//第二個表第一個函數 
  fun = (Fun)*((int*)(*(int*)pB)); 
  fun();//pB指向的表的第一個函數 
  return 0; 
}

哈哈,和我們的猜想完全分歧:

FunA1C
FunA2
FunB
FunB 我們可以畫出這樣的虛函數圖:          暫且這樣了解,編譯器執行B *pB = &objC時不是僅僅是賦值,而是做了相應的優化,將pB指向了第二張虛表。 說了這麼多,我是只是復雜地解釋了虛函數的完成原理,可終究對象的外部的內存規劃是怎樣的?類數據成員與多個虛表的詳細內存規劃又是怎樣的?編譯器是如何在賦值的時分作了優化的呢?我在當前的時間裡會講一下。

以上就是為大家帶來的C++中的多態與虛函數的外部完成辦法全部內容了,希望大家多多支持~

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved