天天看點

C++ 中函數的一些概念

梳理一些C++中函數的一些概念,比如隐藏,想說清隐藏,就肯定要提到重載,覆寫。

先說重載的一些特征:

1),在相同的範圍内,也即是要在同一個類中,成員函數之間的重載。

2),函數名字相同,但是參數不同。

3),virtual關鍵字可以有,可以沒有,

從以上特征看出,重載函數的區分是根據參數的不同,編譯器會根據參數為每個重載函數産生不同的内部辨別符。比如函數:

void func(int x, int y);

void func(char ch);

将産生像_func_int_int, _func_char之類的内部辨別符。

重載是C++中的概念,重載函數也是c++中才有的,那麼假如有一個C函數的聲明是:

void func(int x, int y);

在C++中要調用這個相同的已經編譯好的C函數,怎麼做?

這時要使用一個連接配接交換制定符号 extern "C", 來解決。C中的函數被編譯後在庫中的辨別符是_func,跟C++中編譯後的名字不同。

用extern "C" 就是告訴C++編譯器,函數func是一個C連接配接,應該在庫中找名字_func,而不是找_func_int_int 。

在說覆寫的一些特征:

覆寫是指派生類函數覆寫基類函數。

1)在不同的範圍内,一個在基類,一個在派生類中。

2)函數的名字相同,參數也相同。

3)基類函數必須有virutal關鍵字。

區分了重載和覆寫,再來看下隐藏的概念:

隐藏,是指派生類的函數屏蔽了與其同名的基類函數。

1)如果派生類中函數與基類的函數同名,參數也相同,隻要基類函數沒有virtual關鍵字,那麼基類的函數就被隐藏了,如果基類函數有virtual關鍵字,就是覆寫了。

2)如果派生類的函數與基類的函數同名,但是參數不同,那麼基類的函數就被隐藏了,這裡不是重載,因為重載是在同一個類中。

那麼隐藏和覆寫的差別在哪?以例子說明:

#include <iostream>
#include <stdlib.h>
using namespace std;

class BaseClass{
	public:
	virtual void funcOverride(float num){
		cout << "BaseClass::funcOverride(float),,," << num << endl;
	}
	
	void funcHide(float num){
		cout << "BaseClass::funcHide(float),,," << num << endl;
	}

	void funcHideOrOverride(float num){
		cout << "BaseClass::funcHideOrOverride(float),,," << num << endl;
	}
};

class DerivedClass : public BaseClass{
	public:
	void funcOverride(float num){
		cout << "DerivedClass::funcOverride(float),,," << num << endl;
	}
	
	void funcHide(int num){
		cout << "DerivedClass::funcHide(float),,," << num << endl;
	}

	void funcHideOrOverride(float num){
		cout << "DerivedClass::funcHideOrOverride(float),,," << num << endl;
	}
};

int main(int argc, char *argv[]){
	DerivedClass dC; 
	BaseClass *pBC = &dC;

	pBC->funcOverride(1.99f); //覆寫
	dC.funcOverride(1.99f); //覆寫

	pBC->funcHide(1.99f); //隐藏
	dC.funcHide(1.99f); //隐藏

	pBC->funcHideOrOverride(1.99f); //隐藏
	dC.funcHideOrOverride(1.99f); //隐藏
	return 0;
}
           

運作結果:

DerivedClass::funcOverride(float),,,1.99

DerivedClass::funcOverride(float),,,1.99

BaseClass::funcHide(float),,,1.99

DerivedClass::funcHide(float),,,1

BaseClass::funcHideOrOverride(float),,,1.99

DerivedClass::funcHideOrOverride(float),,,1.99

先說對 funcOverride 方法的調用,繼承類覆寫了基類的方法,是以用基類指針對象pBC調用時,執行了繼承類的方法,這也就是C++中的動态綁定,c++中的函數調用預設不使用動态綁定,如果要觸發動态綁定,必須要滿足兩個條件:一、隻有指定為虛函數的成員函數才能進行動态綁定, 成員函數預設是非虛函數,非虛函數是不進行動态綁定的。二、必須通過基類類型的引用和指針進行函數調用。對比這兩條,pBC->funcOverride(1.99f); 這句調用都滿足,是以它是動态綁定,是多态性的表現。

将基類類型的指針或引用綁定到派生類對象對基對象沒有影響,對象本身不會改變,仍為派生類對象。對象的實際類型可能不同于該對象引用或指針的靜态類型,隻是c++中動态綁定的關鍵。

所謂靜态類型 static type,就是在編譯時可知的引用類型或指針類型。

動态類型 dynamic type ,是指 指針或引用所綁定的對象的類型,這是僅在運作時可知的。

然後說 funcHide,funcHideOrOverride 這兩個函數調用,按理說 pBC 這個基類指針綁定到了派生類對象,應該調用的是派生類的成員函數,但是卻調用了基類的成員函數。原因是:如果函數是非虛函數,則無論實際對象是什麼類型,都執行基類類型所定義的函數,隻有調用虛函數時,才會根據實際綁定的對象類型,調用其定義的函數版本。

以上是覆寫,多态性的一些性質。

下面來說下隐藏,感覺跟多态、覆寫沒什麼關系,它是指基本的函數不可見了,本來繼承類以public的方式繼承了基類,那麼基類的public的方法和屬性,繼承類都是可以直接使用的,但是如果繼承類隐藏了基類的一個函數,即使這個函數在基類是public的,繼承類也無法調用到,看下面的代碼:

class FartherClass{
	public:
	void funcHide(char *num){
		cout << "FartherClass::funcHide(char),,," << num << endl;
	}	
	void funcInherit(char *num){
		cout << "FartherClass::funcInherit(char),,," << num << endl;
	}	
};
           

這裡添加了一個新的基類:FartherClass,

class DerivedClass : public BaseClass, public FartherClass{
	public:
	void funcOverride(float num){
		cout << "DerivedClass::funcOverride(float),,," << num << endl;
	}
	
	void funcHide(int num){
		cout << "DerivedClass::funcHide(int),,," << num << endl;
	}

	void funcHideOrOverride(float num){
		cout << "DerivedClass::funcHideOrOverride(float),,," << num << endl;
	}
};
           

派生類DerivedClass是public的方式繼承了FartherClass,根據前面的描述,funcHide函數隐藏了FartherClass的funcHide函數,是以派生類DerivedClass可以調用FartherClassx的funcInherit函數,但是無法調用到FartherClass的funcHide函數的,

int main(int argc, char *argv[]){
	DerivedClass dC; 
	BaseClass *pBC = &dC;
	char str[]="ABC";

        dC.funcInherit(str);
	dC.funcHide(str);
	return 0;
}
           

這裡的dC.funcHide(str);會報編譯錯誤,invalid conversion from ‘char*’ to ‘int’ [-fpermissive]   dC.funcHide(str); 因為隐藏了父類funcHide(char *num)函數,實際調用的是自身的funcHide(int num)函數,參數由char* 到int無法轉換,是以編譯報錯。

在這裡,可能你真的是想調用基類的以字元指針為參數的函數,隻是寫錯了參數,這時候隐藏規則給了你很好的提示,還有C++是多繼承的,當有多個基類時,有時候可能搞不清楚那些基類定義了跟子類同名的函數,有了隐藏規則,調用時就會及時發現問題。