C/C++中虛函數(shù)詳解及其作用介紹
概述
虛函數(shù) (virtual function) 指可以被子類繼承和覆蓋的函數(shù).

使用方法
基類聲明成員函數(shù)為虛函數(shù)的方法:
virtual [類型] 函數(shù)名([參數(shù)表列])
注: 在類外定義虛函數(shù)時, 不需再加 virtual.
虛函數(shù)的特點(diǎn):
- 提高程序擴(kuò)充性: 派生類根據(jù)需要可以進(jìn)行函數(shù)覆蓋
- 成員函數(shù)被聲明為虛數(shù)后, 其派生類中覆蓋函數(shù)自動稱為虛函數(shù)
- 若虛函數(shù)在派生類中未重新定義, 則派生類簡單繼承其直接基類的虛函數(shù)
- 指向基類的指針, 當(dāng)指向派生類對象時, 可以嗲用派生類的方法
關(guān)聯(lián)
通過關(guān)聯(lián) (binding), 我們可以把一個標(biāo)識符和一個存儲地址聯(lián)系起來, 或者把一個函數(shù)名與一個類對象捆綁在一起.

靜態(tài)關(guān)聯(lián)
靜態(tài)關(guān)聯(lián) (static binding) 指通過對象名調(diào)用虛函數(shù). 在編譯時即可確定其調(diào)用的虛函數(shù)屬于哪一類
動態(tài)關(guān)聯(lián)
動態(tài)關(guān)聯(lián) (dynamic binding) 是指通過基類指針與虛函數(shù), 在運(yùn)行階段確定關(guān)聯(lián)關(guān)系. 動態(tài)關(guān)聯(lián)提供動態(tài)的多態(tài)性, 即運(yùn)行階段的多態(tài)性.
案例1
未使用虛函數(shù)
Square 類:
#ifndef PROJECT6_SQUARE_H
#define PROJECT6_SQUARE_H
class Square {
protected:
int length;
public:
Square(int l) : length(l) {};
int area() const {
return length *length;
}
};
#endif //PROJECT6_SQUARE_H
Rectangle 類:
#ifndef PROJECT6_RECTANGLE_H
#define PROJECT6_RECTANGLE_H
#include "Square.h"
class Rectangle : public Square{
private:
int height;
public:
Rectangle(int l, int h) : Square(l), height(h) {};
int area() const {
return Square::area() * 2 + length * height * 4; // 兩個底加四個邊
}
};
#endif //PROJECT6_RECTANGLE_H
main:
#include <iostream>
#include "Square.h"
#include "Rectangle.h"
using namespace std;
int main() {
// 創(chuàng)建對象
Square s1(2), *pt;
Rectangle r1(3, 3);
pt = &s1;
cout << pt->area() << endl;
pt = &r1;
cout << pt->area() << endl;
return 0;
}
輸出結(jié)果:
4
9 // 輸出的是底面積
此時調(diào)用的是 Square 類的area()函數(shù).
使用虛擬類
Square 類:
#ifndef PROJECT6_SQUARE_H
#define PROJECT6_SQUARE_H
class Square {
protected:
int length;
public:
Square(int l) : length(l) {};
virtual int area() const {
return length *length;
}
};
#endif //PROJECT6_SQUARE_H
Rectangle 類:
#ifndef PROJECT6_RECTANGLE_H
#define PROJECT6_RECTANGLE_H
#include "Square.h"
class Rectangle : public Square{
private:
int height;
public:
Rectangle(int l, int h) : Square(l), height(h) {};
int area() const {
return Square::area() * 2 + length * height * 4; // 兩個底加四個邊
}
};
#endif //PROJECT6_RECTANGLE_H
main:
#include <iostream>
#include "Square.h"
#include "Rectangle.h"
using namespace std;
int main() {
// 創(chuàng)建對象
Square s1(2), *pt;
Rectangle r1(3, 3);
pt = &s1;
cout << pt->area() << endl;
pt = &r1;
cout << pt->area() << endl;
return 0;
}
輸出結(jié)果:
4
54 // 長方體的面積
此時調(diào)用的是 Rectangle 類的area()函數(shù).
案例2
Animal 類:
#ifndef PROJECT6_ANIMAL_H
#define PROJECT6_ANIMAL_H
#include <iostream>
using namespace std;
class Animal {
public:
virtual void bark(){
cout << "咋叫?" << endl;
}
};
#endif //PROJECT6_ANIMAL_H
Dog 類:
#ifndef PROJECT6_DOG_H
#define PROJECT6_DOG_H
#include "Animal.h"
class Dog : public Animal{
public:
void bark() {
cout << "汪汪!" << endl;
}
};
#endif //PROJECT6_DOG_H
Cat 類:
#ifndef PROJECT6_CAT_H
#define PROJECT6_CAT_H
#include "Animal.h"
class Cat : public Animal{
public:
void bark() {
cout << "喵喵!" << endl;
}
};
#endif //PROJECT6_CAT_H
Pig 類:
#ifndef PROJECT6_PIG_H
#define PROJECT6_PIG_H
#include "Animal.h"
class Pig : public Animal {
public:
void bark() {
cout << "哼哼!" << endl;
}
};
#endif //PROJECT6_PIG_H
main:
#include <iostream>
#include "Animal.h"
#include "Dog.h"
#include "Cat.h"
#include "Pig.h"
using namespace std;
int main() {
// 創(chuàng)建對象
Animal a, *pt;
Dog d;
Cat c;
Pig p;
pt = &a;
pt -> bark(); // 調(diào)用基類的bark()
pt = &d;
pt -> bark(); // 調(diào)用狗的bark()
pt = &c;
pt -> bark(); // 調(diào)用貓的bark()
pt = &p;
pt -> bark(); // 調(diào)用豬的bark()
return 0;
}
輸出結(jié)果:
咋叫?
汪汪!
喵喵!
哼哼!
總結(jié)
虛函數(shù)只能是類的成員函數(shù), 而不能將類外的普通函數(shù)聲明為虛函數(shù). 虛函數(shù)的作用是允許在派生類中對基類的虛函數(shù)重新定義 (函數(shù)覆蓋), 只能用于類的繼承層次結(jié)構(gòu)中.
虛函數(shù)能有效減少空間開銷. 當(dāng)一個類帶有虛函數(shù)時, 編譯系統(tǒng)會為該類構(gòu)造一個虛函數(shù)表 (一個指針數(shù)組), 用于存放每個虛函數(shù)的入口地址.
什么時候應(yīng)該使用虛函數(shù):
- 判斷成員函數(shù)所在的類是不是基類, 非基類無需使用虛函數(shù)
- 成員函數(shù)在類被繼承后有沒有可能被更改的功能, 如果希望修改成員函數(shù)功能, 一般在基類中將其聲明為虛函數(shù)
- 我們會通過對象名還是基類指針訪問成員函數(shù), 如果通過基類指針過引用去訪問, 則應(yīng)當(dāng)聲明為虛函數(shù)
有時候在定義虛函數(shù)的時候, 我們無需定義其函數(shù)體. 它的作用只是定義了一個虛函數(shù)名, 具體的功能留給派生類去添加, 也就是純虛函數(shù). 例如我們在上面的 Animal 類的bark()函數(shù)就應(yīng)該聲明為純虛函數(shù), 因?yàn)?Animal 為基類, 定義bark()函數(shù)實(shí)體并無意義.
相關(guān)文章
C++實(shí)踐排序函數(shù)模板項(xiàng)目的參考方法
今天小編就為大家分享一篇關(guān)于C++實(shí)踐排序函數(shù)模板項(xiàng)目的參考方法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-02-02
C語言Iniparser庫實(shí)現(xiàn)ini文件讀寫
iniparser是針對INI文件的解析器。ini文件則是一些系統(tǒng)或者軟件的配置文件。本文就來介紹一下如何利用Iniparser庫實(shí)現(xiàn)ini文件讀寫吧2023-03-03
C++實(shí)現(xiàn)LeetCode(209.最短子數(shù)組之和)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(209.最短子數(shù)組之和),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
C++如何解決rand()函數(shù)生成的隨機(jī)數(shù)每次都一樣的問題
這篇文章主要介紹了C++如何解決rand()函數(shù)生成的隨機(jī)數(shù)每次都一樣的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08

