1. 程式人生 > >從C看C++之(六)多型

從C看C++之(六)多型

 類似LINUX核心驅動子系統,如下面的示意程式碼:

if(fb->open)
    fb->open();
    當我們從更底層對fb->open()進行封裝了的時候,對應的系統呼叫不再是系統預設的,而是呼叫到我們更底層的fb->open.

 這個C++裡面的多型思維有點相仿.如果基類和其派生類都定義了相同(部分相同)的一個方法的話,我們可以選擇呼叫基類或其派生類的一個方法.

    C++的多型主要分兩個方面:編譯時和執行時,兩者分別對應著過載和覆蓋.

過載:

  載函式必須具有不同的引數個數,或不同的引數型別.僅返回值不同時,不能定義為過載函式.

下面給出一個示例.

原始碼:

#include <iostream>

class simpleClass
{
	public:
		void func(char);
		void func(int);
		void func(int,int);
};

void simpleClass::func(char a)
{

	std::cout << "funcChar" << std::endl;

}

void simpleClass::func(int i)
{

	std::cout << "funcInt" << std::endl;

}

void simpleClass::func(int i,int j)
{

	std::cout << "funcIntInt" << std::endl;

}

int main(void)
{
	char ch = 7;
	int i = 77;
	int j = 777;

	simpleClass *cls1 = NULL;

	cls1 = new simpleClass;

	cls1->func(ch);
	cls1->func(i);
	cls1->func(i,j);

	delete cls1;
	cls1 = NULL;
	
	return 0;
}
    編譯執行:
[email protected]:~/learn/Cpp_Program# g++ reload.cpp -o reload
[email protected]:~/learn/Cpp_Program# ./reload 
funcChar
funcInt
funcIntInt
    可見,函式的過載必須是實參的型別或數量不同.

覆蓋:

  覆蓋是指派生類的某函式和基類完全一致,注意這裡是完全一致,而不是像上面的過載一樣要有所區別.過載是編譯時確定下來的,而覆蓋則是在執行時選擇的.此時,該方法需要
加關鍵字"virtual"修飾.至於選擇呼叫是基類的函式還是其派生類的函式的話,由實際的例項物件決定.
 為了加深印象,下面給出非多型的一個示例:

    原始碼:

#include <iostream>

using namespace std;

class baseClass
{
private:
	unsigned int age;
	bool sex;
public:
	baseClass(unsigned int a,bool s)
	{
		age = a;
		sex = s;
	}

	~baseClass()
	{
		std::cout << "~Base Class"<< std::endl;
	}

	unsigned int baseGetAge(void);
	bool baseGetSex(void);

	void display(void);	
};

unsigned int baseClass::baseGetAge(void)
{
	return age;
}

bool baseClass::baseGetSex(void)
{
	return sex;
}

void baseClass::display(void)
{
	std::cout << "base Class Display" << std::endl;
}

class deriveClass:public baseClass
{
private:
	unsigned int score,number;
public:
	deriveClass(unsigned int a,bool se,unsigned int sc,unsigned int nu):baseClass(a,se)
	{
		score = sc;
		number = nu;
	}

	~deriveClass()
	{
		std::cout << "~derive Class" << std::endl;
	}

	unsigned int deriveGetAge(void);
	bool deriveGetSex(void);
	unsigned int deriveGetScore(void);
	unsigned int deriveGetNumber(void);

	void display(void);	
};

unsigned int deriveClass::deriveGetAge(void)
{
	return baseGetAge();
}

bool deriveClass::deriveGetSex(void)
{
	return baseGetSex();
}

unsigned int deriveClass::deriveGetScore(void)
{
	return score;
}

unsigned int deriveClass::deriveGetNumber(void)
{
	return number;
}

void deriveClass::display(void)
{
	std::cout << "derive Class Display" << std::endl;
}


int main(void)
{
	deriveClass *clsDerive = NULL;
	baseClass *clsBase = NULL;

	clsDerive = new deriveClass(18,0,100,26);	
	clsBase = clsDerive;

	std::cout << "Age = " << clsDerive->deriveGetAge()<< std::endl;
	std::cout << "Sex = " << clsDerive->deriveGetSex()<< std::endl;
	std::cout << "Score = " << clsDerive->deriveGetScore()<< std::endl;
	std::cout << "Number = " << clsDerive->deriveGetNumber()<< std::endl;

	clsBase->display();
	clsDerive->display();

	delete clsDerive;
	clsDerive = NULL;

	return 0;
}
    編譯執行:
[email protected]:~/learn/Cpp_Program# g++ class.cpp -o class
[email protected]:~/learn/Cpp_Program# ./class
Age = 18
Sex = 0
Score = 100
Number = 26
base Class Display
derive Class Display
~derive Class
~Base Class
    檢視原始碼,基類及其派生類都有完全一樣的函式display().當用基類指標時(見clsBase->display()),呼叫的是基類的的display();當用派生類指標時(見clsDerive->display()),呼叫的是派生類的display().這很符合常規的邏輯,什麼樣的物件,操作該物件的行為.下面在上述的程式碼裡面只多增加了關鍵字"virtual".如下:
#include <iostream>

using namespace std;

class baseClass
{
private:
	unsigned int age;
	bool sex;
public:
	baseClass(unsigned int a,bool s)
	{
		age = a;
		sex = s;
	}

	~baseClass()
	{
		std::cout << "~Base Class"<< std::endl;
	}

	unsigned int baseGetAge(void);
	bool baseGetSex(void);

	virtual void display(void);	
};

unsigned int baseClass::baseGetAge(void)
{
	return age;
}

bool baseClass::baseGetSex(void)
{
	return sex;
}

void baseClass::display(void)
{
	std::cout << "base Class Display" << std::endl;
}

class deriveClass:public baseClass
{
private:
	unsigned int score,number;
public:
	deriveClass(unsigned int a,bool se,unsigned int sc,unsigned int nu):baseClass(a,se)
	{
		score = sc;
		number = nu;
	}

	~deriveClass()
	{
		std::cout << "~derive Class" << std::endl;
	}

	unsigned int deriveGetAge(void);
	bool deriveGetSex(void);
	unsigned int deriveGetScore(void);
	unsigned int deriveGetNumber(void);

	virtual void display(void);	
};

unsigned int deriveClass::deriveGetAge(void)
{
	return baseGetAge();
}

bool deriveClass::deriveGetSex(void)
{
	return baseGetSex();
}

unsigned int deriveClass::deriveGetScore(void)
{
	return score;
}

unsigned int deriveClass::deriveGetNumber(void)
{
	return number;
}

void deriveClass::display(void)
{
	std::cout << "derive Class Display" << std::endl;
}


int main(void)
{
	deriveClass *clsDerive = NULL;
	baseClass *clsBase = NULL;

	clsDerive = new deriveClass(18,0,100,26);	
	clsBase = clsDerive;

	std::cout << "Age = " << clsDerive->deriveGetAge()<< std::endl;
	std::cout << "Sex = " << clsDerive->deriveGetSex()<< std::endl;
	std::cout << "Score = " << clsDerive->deriveGetScore()<< std::endl;
	std::cout << "Number = " << clsDerive->deriveGetNumber()<< std::endl;

	clsBase->display();
	clsDerive->display();

	delete clsDerive;
	clsDerive = NULL;

	return 0;
}
    編譯執行:
[email protected]:~/learn/Cpp_Program# g++ class.cpp -o class
[email protected]:~/learn/Cpp_Program# ./class
Age = 18
Sex = 0
Score = 100
Number = 26
derive Class Display
derive Class Display
~derive Class
~Base Class
    這次兩次呼叫的都是派生類的display()函式.派生類的關鍵字virtual可以是忽略的不用標識的,但是為了程式的可讀性,最好要加上.

 至此,我們要呼叫派生類的方法有兩種,派生類物件的靜態引用和通過基類指標通過覆蓋(虛擬函式)的方式引用.為了程式的可讀性,個人建議選擇靜態引用.如下:

    原始碼:

#include <iostream>

using namespace std;

class  A
{
protected:	
	int x;
public:	
	A()
	{
		x =1000;
	}   

	virtual void  print()
	{	
		std::cout << "x = " << x << std::endl;
	}//虛擬函式
};

class B:public A
{	
private:
	int y;

public:	B() 
	{ 
		y=2000;
	}

	virtual void  print()
	{
		std::cout << "y = " << y << std::endl;
	}//派生虛擬函式
};	

class C:public A
{
private:
	int z;

public:	
	C()
	{
		z=3000;
	}
	
	virtual void  print()
	{
		std::cout << "z = " << z << std::endl;
	}//派生虛擬函式
};

int main(void)
{  
	A  a, *pa;
	B  b;	C  c;
     	a.print();    
	b.print();	
	c.print();  //靜態呼叫

    	pa=&a;    
	pa->print();//呼叫類A的虛擬函式

    	pa=&b;    
	pa->print();//呼叫類B的虛擬函式

	pa=&c;     
	pa->print();//呼叫類C的虛擬函式

	return 0;
}
    編譯執行:
[email protected]:~/learn/Cpp_Program# g++ reference.cpp -o reference
[email protected]:~/learn/Cpp_Program# ./reference 
x = 1000
y = 2000
z = 3000
x = 1000
y = 2000
z = 3000

純虛擬函式:

    當我們使用到繼承與派生的時候,我們比較關注的並不是基類本身,而是派生類的具體例項,這當然涉及到其中的操作集(函式).多型(覆蓋)的實現,可以實現"一個介面,多種功能實現",從而提高程式的可讀性和複用性.純虛擬函式就是這樣的一個"華而不實"的介面標識.定義如下:

在基類中不對虛擬函式給出有意義的實現,它只是在派生類中有具體的意義.這時基類中的虛擬函式只是一個入口,具體的目的地由不同的派生類中的物件決定.
這個虛擬函式稱為純虛擬函式.
    其定義形式如下:
class    <基類名>
{	virtual <型別><函式名>(<引數表>)=0;
	......
};
 一個簡單的示例程式碼:
class  A{
protected:	int x;
public:	A(){x =1000;}   
	virtual void  print()=0;  //定義純虛擬函式
};
class B:public A{  //派生類
private:   int y;
public:	B(){ y=2000;}
	void  print(){cout <<“y=”<<y<<‘\n’;}//重新定義純虛擬函式
};	
class C:public A{   //派生類
	int z;
public:	C(){z=3000;}
	void  print(){cout <<“z=”<<z<<‘\n’;}//重新定義純虛擬函式
};
void  main(void )
{   A  *pa;	B  b;  	C  c;     
    pa=&b;    pa->print();	pa=&c;    pa->print();
    A  a;     pa=&a;       pa->print( );
}
小結:

    可見,過載、覆蓋二者的區別為:

選擇時機:
    過載:編譯時決定;
    覆蓋:執行時決定.
存在形式:
    過載:部分相同;
    覆蓋:完全一致.


相關推薦

CC()

 類似LINUX核心驅動子系統,如下面的示意程式碼: if(fb->open) fb->open();    當我們從更底層對fb->open()進行封裝了的時候,對應的系統呼叫不再是系統預設的,而是呼叫到我們更底層的fb->open.

CC(八)檔案流操作

    Linux平臺下提供了標準的C庫API實現對檔案的讀寫操作,同樣C++也提供了自身對檔案流操作的一些手段.雖然不知道以後會不會用到,但是瞭解一下是很有必要的. 原始碼: #include <iostream> #include <fstream&

C++零實現深度神經網路——實戰手寫數字識別(sigmoid和tanh)

本文由@星沉閣冰不語出品,轉載請註明作者和出處。之前的五篇部落格講述的內容應該覆蓋瞭如何編寫神經網路的大部分內容,在經過之前的一系列努力之後,終於可以開始實戰了。試試寫出來的神經網路怎麼樣吧。一、資料準

Keil C51對C語言的關鍵詞擴充套件: compact

函式的引數和區域性變數儲存在記憶體模型指定的預設區域內。 我們可以通過small,compact 或large 指定一個函式使用何種記憶體模型。 #pragma small /* 預設記憶體模型為small*/ extern int calc (char

彙編c語言函式呼叫

學C語言時,就聽老師說函式呼叫時是通過棧來記錄資訊,又聽說什麼“保留現場”,"恢復現場"一些既聽不懂,也不知道怎麼弄懂的東西。最近正在學習Linux下的彙編,現在就通過一個簡單的例子來展示一下彙編級的函式呼叫,這樣能夠增加大家對C語言的理解。雖然並不是很完善,但是足夠闡明函式

C++學習筆記 () ---- 與虛擬函式

①、多型的概念 先上一個示例 #include <iostream> using namespace std; //基類People class People{ public: People(char *name, int age); void display(

壹開始微服務 [ DDD ] ║聚合 與 聚合根 (下)

前言 哈嘍大家週二好,上次咱們說到了實體與值物件的簡單知識,相信大家也是稍微有些瞭解,其實實體咱們平時用的很多了,基本可以和資料庫表進行聯絡,只不過值物件可能不是很熟悉,值物件簡單來說就是在DDD領域驅動設計中,為了更好的展示領域模型之間的關係,制定的一個物件,它沒有狀態和標識,目的就是為了表示一個值。今天

初學程式設計C++動態

程式碼示例: #include<iostream> #include<stdlib.h> #include"Shape.h" #include"Circle.h" #include"Rect.h" using namespace std;

C++學習的理解

最近學習C++多型及子類記憶體結構,有一些理解與看法,記錄下來 1.多型產生,虛擬函式,虛擬函式指標,虛擬函式表 這一部分不詳細描述,個人參考的書籍是Siddhartha Rao的<21天學通C++>的第11章:多型 瞭解了編譯器利用虛擬函式表與物件的虛擬函式指標來實現多型的

菜鳥的C#學習旅——的實現途徑

目錄 一、定義 二、虛方法: 三、抽象類與抽象方法: 四、介面實現: 五、總結: 一、定義 多型:在面嚮物件語言中,介面的多種不同實現方式即為多型 多型性就是指在程式執行時,執行的雖然是一個呼叫方法的語句,卻可以根據派生類物件的型別的不同完成方法不同的具體實現

插圖《程式碼整潔道——程式設計師的職業素養》

《程式碼整潔之道——程式設計師的職業素養》是《程式碼整潔之道》的作者Bob大叔的第二部作品,筆者讀完2016年再版的本書後有感而發寫下本文,本書2012年的版本叫《程式設計師的職業素養》。從內容上看

SpringCloud 菜鳥到大牛 訊息和非同步 MQ

記得架構圖 HTTP 常見是同步 ,但是也是支援非同步的 釋出訂閱模式 MQ應用場景 日誌處理 Kafka 流量削峰 秒殺系列 應用解耦 Spring Cloud Stream RabbitMQ 與 Kafka

mysql數據庫刪庫到跑路mysql表查詢

logs 插入 並且 所有 方式 color dep join 查看表 一 介紹 本節主題 多表連接查詢 復合條件連接查詢 子查詢 準備表 company.employeecompany.department #建表 create table department( id

C++筆記 第四十九課 的概念和意義---狄泰學院

如果在閱讀過程中發現有錯誤,望評論指正,希望大家一起學習,一起進步。 學習C++編譯環境:Linux 第四十九課 多型的概念和意義 1.函式重寫回顧 父類中被重寫的函式依然會繼承給子類 子類中重寫的函式將覆蓋父類中的函式 通過作用域分辨符(::)可以訪問到父類中的函式

(C/C++學習心得)5.C++中的虛繼承-虛擬函式-解析

1 #include<iostream> 2 using namespace std; 3 4 class bed 5 { 6 public: 7 bed(float l,float wi,float we) 8 :len(l),wid(w

C++類物件中虛擬函式與性的實現

在面向物件程式設計時,有時會遇到這種需求:我們希望同一個方法在基類和派生類中實現不同的功能,即體現出行為上的多型性。一般有兩種方法可以實現這種需求,其一是在派生類中重新定義基類中方法,其二是使用虛擬函式。這裡主要記錄利用虛擬函式實現多型性的方法。 類中虛擬函式的定義方法 虛擬函式

C++學習筆記】虛擬函式實現原理

源至:https://blog.csdn.net/haoel/article/details/1948051   C++中的虛擬函式的作用主要是實現了多型的機制。關於多型,簡而言之就是用父類型別的指標指向其子類的例項,然後通過父類的指標呼叫實際子類的成員函式。這種技術可以讓父類的指

C#中的介面和繼承

 上一節中我們學習到了建構函式,用來初始化一個物件的例項。同時在.NET Framework中,提供了解構函式用於清理物件。一般情況下不需要特別解構函式,系統會自動提供預設的解構函式來執行操作,清理不再需要的物件。 一、 靜態和例項類成員 屬性,方法和欄位等成員是物件例項所

SpringBoot2.0 環境配置

        開發過程中面對不同的環境,例如資料庫、redis伺服器等的不同,可能會面臨一直需要修改配置的麻煩中,在以前的專案中,曾通過Tomcat的配置來實現,有的專案甚至需要手動修改相關配置,這種方式費時費力,出錯的概率還極大,SpringBoot為我們提供了更加簡單方

C++中的過載,覆蓋,隱藏與

過載,覆蓋,隱藏與多型是C++中面向物件設計的幾大特性.深入理解這些特性,對於我們編寫高效的可複用,可重用的程式碼有重要意義.這裡,對這一部分知識加以回顧. 過載發生在同一個類當中,當有同名函式,但函式引數不同時,就發生函式的過載. 覆蓋發生在基類與派生類當