程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C++中的重載、掩蓋、隱藏引見

C++中的重載、掩蓋、隱藏引見

編輯:關於C++

C++中的重載、掩蓋、隱藏引見。本站提示廣大學習愛好者:(C++中的重載、掩蓋、隱藏引見)文章只能為提供參考,不一定能成為您想要的結果。以下是C++中的重載、掩蓋、隱藏引見正文


前幾天面試時被問及C++中的掩蓋、隱藏,概念根本答不下去,只答了怎樣用指針完成多態,也還有脫漏。最終不歡而散。回來後在網上查找學習了一番,做了這個總結。其中局部文字借用了他人的博客,望不要見怪。

•概念

一、重載(overload)
指函數名相反,但是它的參數表列個數或順序,類型不同。但是不能靠前往類型來判別。
(1)相反的范圍(在同一個作用域中) ;
(2)函數名字相反;
(3)參數不同;
(4)virtual 關鍵字可有可無。
(5)前往值可以不同;

二、重寫(也稱為掩蓋 override)
是指派生類重新定義基類的虛函數,特征是:
(1)不在同一個作用域(辨別位於派生類與基類) ;
(2)函數名字相反;
(3)參數相反;
(4)基類函數必需有 virtual 關鍵字,不能有 static 。
(5)前往值相反(或是協變),否則報錯;<—-協變這個概念我也是第一次才知道…

(6)重寫函數的訪問修飾符可以不同。雖然 virtual 是 private 的,派生類中重寫改寫為 public,protected 也是可以的

三、重定義(也成隱藏)
(1)不在同一個作用域(辨別位於派生類與基類) ;
(2)函數名字相反;
(3)前往值可以不同;
(4)參數不同。此時,不管有無 virtual 關鍵字,基類的函數將被隱藏(留意別與重載以及掩蓋混雜) 。
(5)參數相反,但是基類函數沒有 virtual關鍵字。此時,基類的函數被隱藏(留意別與掩蓋混雜) 。

•例子

#include <iostream>
using namespace std;
class SParent
{
public:
  SParent( ){};
  SParent( const SParent &p )
  {
    cout << "parent copy construct" << endl;
  }
  int add( int a,int b )
  {
    cout << "parent int add" << endl;
    return a + b;
  }
  double add( double a,double b )
  {
    cout << "parent double add" << endl;
    return a + b;
  }
  virtual int dec( int a,int b )
  {
    cout << "parent int dec" << endl;
    return a - b;
  }
};
class SChild : public SParent
{
public:
  //using SParent::add;
  float add( float a,float b )
  {
    cout << "child float add" << endl;
    return a + b;
  }
  int dec(int a, int b)
  {
    cout << "child int dec" << endl;
    return a - b;
  }
};
int main()
{
  /* 測試重載 */
  SParent parent;
  parent.add( 3,5 );
  parent.add( (double)3,(double)5 );
  cout << endl;
  /* 測試掩蓋 */
  SChild *pchild = (SChild *)new SParent();/* 基類強轉為子類...風險...,用dynamic_cast轉換也不行 */
  pchild->dec( 10,3 );
  SParent *pparent = new SChild();
  pparent->dec( 11,3 );
  cout << endl;
  /* 測試隱藏 */
  SChild child;
  child.add( (int)3,(int)5 );
  cout << endl;
  /* 測試函數表 */
  ((SParent *)NULL)->add( 4,6 );
  ((SChild *)NULL)->add( 4,6 );
  int a = 0;
  ((SChild *)&a)->add( 4,6 );
   cout << endl;
  /* 測試函數地址 */
  ((SParent)child).add( (int)4,(int)8 );
  child.SParent::add( 3,5 );

  return 0;
}

輸入後果:

parent int add
parent double add

parent int dec
child int dec

child float add

parent int add
child float add
child float add

parent copy construct
parent int add
parent int add
按 <RETURN> 來封閉窗口...

•了解
int SParent::add(int a,int b)與double SParent::add( double a,double b )是重載

int SParent::add(int a,int b)與double SParent::add( double a,double b )都被子類SChild中的float SChild::add( float a,float b )隱藏

int SParent::dec( int a,int b )被子類SChild中的int SChild::dec( int a,int b )掩蓋

•測試

1.重載測試,復雜易懂,略過。
2.掩蓋測試。dec函數在基類、子類中同名同參,為虛函數,故稱掩蓋。

SChild *pchild = (SChild *)new SParent()創立的是一個基類對象,其函數表應該為

SParent *pparent = new SChild();創立一個子類對象,其函數表應該為

由下面的函數表可見,當發作掩蓋時,子類的函數名會把基類的同名函數掩蓋(這也就是為什麼叫掩蓋的緣由吧)。這樣我們可以應用一個指向子類的基類指針完成多態。但重點只要一
個,就是函數表裡究竟指向誰(不論這個指針經過轉換後是什麼類型的).故輸入辨別為父類、子類。這是一個運轉時多態。

3.隱藏測試

int SParent::add(int a,int b)與double SParent::add( double a,double b )都被子類SChild中的float SChild::add( float a,float b )掩蓋,是由於他們同名,而且在不同的作用域中(基類、子類作用域是不同的)。child.add( (int)3,(int)5 );這行代碼中,編譯器在子類中查找add函數,只找到了一個(基類的add(int a,int b)會被編譯依據隱藏規則略過),再依據隱式類型轉換發現該函數適用。假如無法隱式轉換,則編譯不過。隱藏的緣由:避免隱式類型轉換形成錯誤。比方int也是可以轉換成char的,假設基類有一函數add(char a,char b),子類也有一函數add(double a,double b)。順序員想著在子類隱式把int轉換為double,但編譯器能夠調的是基類的。這也避免了一些庫或封裝好的基類對順序員形成困擾。

  像下面的代碼,假如你的確需求基類的函數,可以用using SParent:add。則把基類的add函數域擴展到了子類,構成重載。

4.函數表測試

下面我們說到函數表,這個是在編譯時定好的,順序運轉時加載到內存中。這意味著我們可以直接經過地址去調用函數。所以((SChild *)NULL)->add( 4,6 );這種代碼也是能運轉經過的。網上還有人經過計算直接取到了函數表的地址直接調用了。但這種代碼不平安不標准不說,還有個更大的問題。當成員函數裡需求調用成員變量時,經過這種假的對象指針一定找不到成員變量表,直接訪問了合法內存。

5.函數地址測試

有了隱藏、掩蓋,哪麼我們要怎樣調用被隱藏、掩蓋的函數呢。上面有兩種辦法:

((SParent)child).add( (int)4,(int)8 );
child.SParent::add( 3,5 );

第一種是比擬低效的辦法。現實上它是經過拷貝結構函數生成一個暫時的基類變量去調用基類的add函數。
第二種經過::指定域去訪問。這種辦法是編譯器依據域直接找到了基類的函數地址,跟函數表沒有多大關系。

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