<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
多型性是物件導向程式設計的關鍵技術之一,若程式設計語言不支援多型性,不能稱為物件導向的語言,利用多型性技術,可以呼叫同一個函數名的函數,實現完全不同的功能
在C++中有兩種多型性:
通過函數的過載和運運算元的過載來實現的
執行時的多型性是指在程式執行前,無法根據函數名和引數來確定該呼叫哪一個函數,必須在程式執行過程中,根據執行的具體情況來動態地確定;它是通過類繼承關係public和虛擬函式來實現的,目的也是建立一種通用的程式;通用性是程式追求的主要目標之一
通過參照或指標呼叫時,才可以達到執行時的多型
虛擬函式是一個類的成員函數,定義格式如下:
virtual 返回型別 函數名(參數列);
關鍵字virtual
指明該成員函數為虛擬函式,virtual僅用於類定義中,如虛擬函式在類外定義,不可加virtual
我們來看下面程式碼
class Animal { private: string name; public: Animal(const string& na):name(na) {} public: virtual void eat(){} virtual void walk(){} virtual void tail(){} virtual void PrintInfo(){} string& get_name() { return name; } const string& get_name()const { return name; } }; class Dog :public Animal { private: string owner; public: Dog(const string& ow, const string na) :Animal(na), owner(ow) {} virtual void eat() { cout << "Dog Eat: bone" << endl; } virtual void walk() { cout << "Dog Walk: run" << endl; } virtual void tail() { cout << "Dog Tail: wangwang" << endl; } virtual void PrintInfo() { cout << "Dog owner" << owner << endl; cout << "Dog name:" << get_name() << endl; } }; class Cat :public Animal { private: string owner; public: Cat(const string& ow, const string na) :Animal(na), owner(ow) {} virtual void eat() { cout << "Cat Eat: fish" << endl; } virtual void walk() { cout << "Cat Walk: silent" << endl; } virtual void tail() { cout << "Cat Tail: miaomiao" << endl; } virtual void PrintInfo() { cout << "Cat owner: " << owner << endl; cout << "Cat name: " << get_name() << endl; } }; // 需要公有繼承 公有繼承代表是一個的意思 // 需要參照或指標呼叫 void fun(Animal& animal) { animal.eat(); //物件名稱.虛方法() animal.walk(); animal.tail(); animal.PrintInfo(); } int main() { Dog dog("zyq", "hashiqi"); //const string& ow = "zyq" Cat cat("zyq", "bosimao"); fun(dog); fun(cat); return 0; }
在這裡我們可以看到,當我們呼叫fun()函數時,傳入dog物件則呼叫Dog的方法,傳入cat呼叫Cat方法;這就是所謂的執行時的多型
要想達到執行時的多型(晚繫結)需要滿足:
若發生早繫結,則會呼叫Animal型別的方法
成員函數應儘可能的設定為虛擬函式,但必須注意一下幾條:
1.派生類中定義虛擬函式必須與基礎類別中的虛擬函式同名外,還必須同參數列,同返回型別;否則被認為是過載,而不是虛擬函式。如基礎類別中返回基礎類別指標,派生類中返回派生類指標是允許的,這是一個例外
2.只有類的成員函數才能說明為虛擬函式,這是因為虛擬函式僅適用於有繼承關係的類物件
3.靜態成員函數,是所有同一類物件公有,不受限於某個物件,不能作為虛擬函式(友元函數也不可以)
4.實現動態多型性時,必須使用基礎類別型別的指標變數或參照,使該指標指向該基礎類別的不同派生類的物件,並通過該指標指向虛擬函式,才能實現動態的多型性
5.行內函式每個物件一個拷貝,無對映關係,不能作為虛擬函式
6.解構函式可定義為虛擬函式,建構函式不可以定義為虛擬函式,因為在呼叫建構函式時物件還沒有完成範例化;在基礎類別中及其派生類中都動態分配的記憶體空間時,必須把解構函式定義為虛擬函式,實現復原物件時的多型性
7.函數執行速度要稍慢一些,為了實現多型性,每一個派生類中均要儲存相應虛擬函式的入口地址表,函數的呼叫機制也是間接實現;所以多型性總要付出一定代價,但通用性是一個更高的目標
8.如果定義放在類外,virtual只能加在函數宣告前面,不能載入函數定義前面;正確的定義必須不包括virtual
虛擬函式是覆蓋,同名函數是隱藏
虛擬函式編譯過程
class Object { private: int value; public: Object(int x = 0) :value(x) {} virtual void add() { cout << "Object::add" << endl; } virtual void fun() { cout << "Object::fun" << endl; } virtual void print()const { cout << "Object::print" << endl; } }; class Base:public Object { private: int sum; public: Base(int x = 0) :Object(x+10),sum(x) {} virtual void add() { cout << "Base::add" << endl; } virtual void fun() { cout << "Base::fun" << endl; } virtual void print()const { cout << "Base::print" << endl; } }; int main() { }
此處虛擬函式表中進行的是同名覆蓋,而不像繼承關係中,同名成員進行隱藏,就近處理;虛函表僅有一份,存在資料區
在主函數建立物件
int main() { Base base(10); Object* op = &base; }
可以看到base的大小為12位元組,因為其中基礎類別物件Object,新增了虛表變為了8位元組,且在構建過程,首先構建Object基礎類別,此時虛表指標指向Object的虛表,而接著構建Base類的時候,會將虛表指標修改為指向Base的虛表
也就是,當有虛擬函式時,建構函式除了構建物件初始化物件的資料成員外,還會將虛表的地址給到虛表指標;同時這也是建構函式不可以作為虛擬函式的原因
int main() { Base base(10); Object* op = NULL; Object obj(0); op = &base; op->add(); //指標或參照調動,則採用執行時多型 op->fun(); op->print(); obj = base; obj.add(); //物件直接調動,則採用編譯時多型 obj.fun(); obj.print(); }
也就是我們通過,物件名.方法
的方式呼叫虛擬函式,則通過編譯時多型的方式
執行時的多型,是通過查詢虛表進行呼叫;下面通過組合進一步檢視
只有進行以指標呼叫或參照呼叫的時候才會對虛表進行查詢
三層繼承
class Object { private: int value; public: Object(int x = 0) :value(x) {} virtual void add() { cout << "Object::add" << endl; } virtual void fun() { cout << "Object::fun" << endl; } virtual void print()const { cout << "Object::print" << endl; } void fn_a() { fun(); } }; class Base:public Object { private: int sum; public: Base(int x = 0) :Object(x+10),sum(x) {} virtual void add() { cout << "Base::add" << endl; } virtual void fun() { cout << "Base::fun" << endl; } virtual void show() { cout << "Base::show" << endl; } }; class Test :public Base { private: int num; public: Test(int x = 0) :Base(x + 10) {} virtual void add() { cout << "Test::add" << endl; } virtual void print() const { cout << "Test::print" << endl; } virtual void show() { cout << "Test::show" << endl; } };
我們可以看到虛擬函式表,當我們構建派生類,會複製基礎類別的虛擬函式表,將虛表指標指向新的虛擬函式表,並且將同名的虛擬函式進行覆蓋
依舊使用上面程式碼
/* void fn_a() { fun(); //this->fun(); 屬於動態繫結! } */ int main() { Test t1; Base base; Object obj; t1.fn_a(); //fn_a(&t1); base.fun_a(); obj.fn_a(); return 0; }
這裡依然屬於動態繫結,所以呼叫虛表指標指向的相對應類的虛表
本篇文章就到這裡了,希望能夠給你帶來幫助,也希望您能夠多多關注it145.com的更多內容!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45