虛函數與多態,函數多態
假設我們有三個類Person、Teacher、Student它們之間的關係如下:
類的關係圖
普通成員函數
【Demo1】
根據這個類圖,我們有下面的代碼實現
#ifndef __OBJEDT_H__#define __OBJEDT_H__#include <string>#include <iostream>class Person{public: Person(const string& name, int age) : m_name(name), m_age(age) { } void ShowInfo() { cout << "姓名:" << m_name << endl; cout << "年齡:" << m_age << endl; }protected: string m_name; //姓名 int m_age; //年齡};class Teacher : public Person{public: Teacher(const string& name, int age, const string& title) : Person(name, age), m_title(title) { } void ShowInfo() { cout << "姓名:" << m_name << endl; cout << "年齡:" << m_age << endl; cout << "職稱:" << m_title << endl; }private: string m_title; //職稱};class Student : public Person{public: Student(const string& name, int age, int studyId) : Person(name, age), m_studyId(studyId) { } void ShowInfo() { cout << "姓名:" << m_name << endl; cout << "年齡:" << m_age << endl; cout << "學號:" << m_studyId << endl; }private: int m_studyId; //學號};#endif //__OBJEDT_H__
測試代碼:
void test(){ Person* pPerson = new Person("張三", 22); Teacher* pTeacher = new Teacher("李四", 35, "副教授"); Student* pStudent = new Student("王五", 18, 20151653); pPerson->ShowInfo(); cout << endl; pTeacher->ShowInfo(); cout << endl; pStudent->ShowInfo(); cout << endl; delete pPerson; delete pTeacher; delete pStudent;}
結果:
姓名:張三
年齡:22
姓名:李四
年齡:35
職稱:副教授
姓名:王五
年齡:18
學號:20151653
說明:
這裡的ShowInfo就是一個普通的函數。pPerson、pTeacher和pStudent三個對象調用ShowInfo分別展示自己的資訊。
我們知道:父類的指標是可以指向子類的對象的。我們把上面的測試代碼稍微改一下:
【Demo2】
void test(){ Person* pPerson = new Person("張三", 22); Person* pTeacher = new Teacher("李四", 35, "副教授"); Person* pStudent = new Student("王五", 18, 20151653); pPerson->ShowInfo(); cout << endl; pTeacher->ShowInfo(); cout << endl; pStudent->ShowInfo(); cout << endl; delete pPerson; delete pTeacher; delete pStudent;}
結果:
姓名:張三
年齡:22
姓名:李四
年齡:35
姓名:王五
年齡:18
這時,pTeacher和pStudent只輸出了姓名和年齡,並沒有輸出子類所具有的特性(職稱和學號)。這應該不是你期望的結果,你可能期望pTeacher和pStudent輸出老師和學生的完整資訊,這時就需要用虛函數。
虛函數
我們把Person中的ShowInfo成員改成虛函數(在前面加上virtual),代碼如下:
【Demo3】
class Person{public: Person(const string& name, int age) : m_name(name), m_age(age) { } virtual void ShowInfo() { cout << "姓名:" << m_name << endl; cout << "年齡:" << m_age << endl; }protected: string m_name; //姓名 int m_age; //年齡};
在執行上面【Demo2】中的測試代碼,得到我們想到的結果:
姓名:張三
年齡:22
姓名:李四
年齡:35
職稱:副教授
姓名:王五
年齡:18
學號:20151653
虛函數用法要點:
【Demo4】:針對第4點說明:
class Person{public: Person(const string& name, int age) : m_name(name), m_age(age) { } virtual void ShowInfo() { cout << "姓名:" << m_name << endl; cout << "年齡:" << m_age << endl; } string GetName(); //正確,普通函數如果不被使用,可以只有聲明沒有定義 virtual int GetAge(); //錯誤,虛函數必須要有定義,即使是一個空實現,因為編譯器無法確定會使用哪個函數protected: string m_name; //姓名 int m_age; //年齡};
【Demo5】:針對第5點進行說明:
設計我們的類如下定義。
class Person{public: virtual void SetAge(int age = 0) { m_age = age; } //... 省略};class Teacher : public Person{public: virtual void SetAge(int age = 1) { m_age = age; } //... 省略};class Student : public Person{public: virtual void SetAge(int age = 2) { m_age = age; } //... 省略};
測試1:
void test(){ Person* pPerson = new Person("張三", 22); Teacher* pTeacher = new Teacher("李四", 35, "副教授"); Student* pStudent = new Student("王五", 18, 20151653); pPerson->SetAge(); pTeacher->SetAge(); pStudent->SetAge(); pPerson->ShowInfo(); cout << endl; pTeacher->ShowInfo(); cout << endl; pStudent->ShowInfo(); cout << endl; delete pPerson; delete pTeacher; delete pStudent;}
結果:
姓名:張三
年齡:0
姓名:李四
年齡:1
職稱:副教授
姓名:王五
年齡:2
學號:20151653
測試2:
void test(){ Person* pPerson = new Person("張三", 22); Person* pTeacher = new Teacher("李四", 35, "副教授"); Person* pStudent = new Student("王五", 18, 20151653); pPerson->SetAge(); pTeacher->SetAge(); pStudent->SetAge(); pPerson->ShowInfo(); cout << endl; pTeacher->ShowInfo(); cout << endl; pStudent->ShowInfo(); cout << endl; delete pPerson; delete pTeacher; delete pStudent;}
結果:
姓名:張三
年齡:0
姓名:李四
年齡:0
職稱:副教授
姓名:王五
年齡:0
學號:20151653
純虛函數
在上面的例子中,我們假設所有的人都要工作,但不同的人工作的方式不同。於是我們就要強制要求繼承自Person的子類都要有工作的方法,這就需要純虛函數。定義如下:
【Demo6】
class Person{public: //... 省略 virtual void DoWork() = 0; //... 省略};
但此時我們編譯
Person* pPerson = new Person("張三", 22);
這句話時會報錯:error C2259: ‘Person’ : cannot instantiate abstract class
這是因為我們並沒有為Person實現DoWork方法,而包含純虛函數的類是一個抽象的類,抽象類別不能被執行個體化。
於是我們在子類中對它實現如下:
【Demo7】
class Teacher : public Person{public: //... 省略 virtual void DoWork() { cout << "教書..." << endl; } //... 省略};class Student : public Person{public: //... 省略 virtual void DoWork() { cout << "學習..." << endl; } //... 省略};
沒用DoWork方法:
void test(){ Person* pTeacher = new Teacher("李四", 35, "副教授"); Person* pStudent = new Student("王五", 18, 20151653); pTeacher->DoWork(); cout << endl; pStudent->DoWork(); cout << endl; delete pTeacher; delete pStudent;}
結果:
教書…
學習…
純虛函數用法要點:
著作權聲明:本文為博主原創文章,未經博主允許不得用於任何商業用途,轉載請註明出處。