1.所謂多態(tài)
多態(tài)就是一種特殊的機(jī)制,執(zhí)行這種機(jī)制的結(jié)果就是,調(diào)用同一個(gè)接口,在不同的情況下
可以實(shí)現(xiàn)不同的功能。
(1)c語(yǔ)言模擬的多態(tài)
講c語(yǔ)言時(shí)強(qiáng)調(diào)過(guò),實(shí)際上c語(yǔ)言也是可以模擬多態(tài),主要時(shí)利用函數(shù)指針
進(jìn)行函數(shù)的回掉實(shí)現(xiàn)的,這個(gè)實(shí)現(xiàn)過(guò)程往往會(huì)借助于結(jié)構(gòu)體進(jìn)行封裝實(shí)現(xiàn)。
(2)面向?qū)ο笳Z(yǔ)言的多態(tài)
在面向?qū)ο蟮恼Z(yǔ)言中多態(tài)的實(shí)現(xiàn)依賴(lài)三個(gè)機(jī)制
(1)繼承
(2)函數(shù)重寫(xiě)
(3)向上轉(zhuǎn)型
(4)動(dòng)態(tài)綁定
以上多態(tài)實(shí)現(xiàn)方式中,不管是那種實(shí)現(xiàn)方式,在邏輯上都實(shí)現(xiàn)了調(diào)用同一個(gè)函數(shù)接口時(shí)調(diào)
用的是不同派生類(lèi)的重寫(xiě)函數(shù),從而得到不同的執(zhí)行結(jié)果。
2.基類(lèi)與派生類(lèi)成員函數(shù)同名
分為兩種情況,一種是函數(shù)簽名完全相同,另一種是同函數(shù)名但不同參數(shù)列表。
(1)函數(shù)簽名完全相同(也叫函數(shù)的重寫(xiě))
(1)子類(lèi)訪(fǎng)問(wèn)基類(lèi)同名函數(shù)時(shí),先使用using進(jìn)行聲明,然后使用
“基類(lèi)名::成員函數(shù)()”的方式進(jìn)行調(diào)用即可
比如:
using People::show;
People::show();
(2)如果基類(lèi)成員函數(shù)是public, 在外界希望對(duì)通過(guò)派生類(lèi)基類(lèi)同名
函數(shù)進(jìn)行訪(fǎng)問(wèn)時(shí),使用static_cast<>()將派生類(lèi)向上強(qiáng)制轉(zhuǎn)換
為基類(lèi)后調(diào)用的就是基類(lèi)的被同名的函數(shù)。
(3)注意:
如果基類(lèi)中該函數(shù)又被重載多次,那么在子類(lèi)內(nèi)部活在在外部希望通過(guò)
子類(lèi)訪(fǎng)問(wèn)父類(lèi)的同名函數(shù)時(shí),可以通過(guò)參數(shù)列表的進(jìn)行重載的區(qū)分區(qū)分
調(diào)用,局提請(qǐng)看下面的例子。
例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <string>
#include <cassert>
#include <errno.h>
using namespace std;
class People {
public:
People(const string name="zhanagsan"):name(name) { }
void show() {
cout << "111基類(lèi)name:"<<name<<endl;
}
//重載的show函數(shù)
void show(int a) {
cout << "222基類(lèi)name:"<<name<<endl;
}
public:
string name;
};
class Student : public People {
public:
Student(const string name="wangwu"):name(name) { }
using People::show;
void show() {
People::show(10);//通過(guò)參數(shù)列表區(qū)分重載函數(shù)
cout << "派生類(lèi)name:"<<name<<endl;
}
public:
string name;
};
int main(void)
{
Student stu1 = Student();
stu1.show();//通過(guò)參數(shù)列表區(qū)分重載函數(shù)
static_cast<People>(stu1).show();//通過(guò)參數(shù)列表區(qū)分重載函數(shù)
return 0;
}
(2)同名但參數(shù)列表不同
(1)這種情況下,如果希望在子類(lèi)中訪(fǎng)問(wèn)基類(lèi)同名但不同參數(shù)列表的
成員函數(shù),針對(duì)這種情況時(shí),以下方式都可以:
比如還是以show函數(shù)為例:
(1)聲明:using People::show;
調(diào)用:People::show(10);
(2)聲明:using People::show;
調(diào)用:show(10);//可以省略People::
(3)調(diào)用:People::show(10);//可以直接省略u(píng)sing聲明
(2)如果希望在外部通過(guò)子類(lèi)訪(fǎng)問(wèn)基類(lèi)的同名但不同參數(shù)列表的成員函數(shù)
時(shí),不需要做向基類(lèi)的轉(zhuǎn)換,可以通過(guò)參數(shù)列表的不同直接區(qū)分
調(diào)用的是基類(lèi)的函數(shù)還是子類(lèi)的函數(shù)。
例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <string>
#include <cassert>
#include <errno.h>
using namespace std;
class People {
public:
People(const string name="zhanagsan"):name(name) { }
void show(int a) {
cout << "基類(lèi)name:"<<name<<endl;
}
public:
string name;
};
class Student : public People {
public:
Student(const string name="wangwu"):name(name) { }
using People::show;
void show() {
People::show(10);
cout << "派生類(lèi)name:"<<name<<endl;
}
public:
string name;
};
int main(void)
{
Student stu1 = Student();
stu1.show();//通過(guò)參數(shù)區(qū)分,調(diào)用的時(shí)子類(lèi)的函數(shù)
stu1.show(10);//通過(guò)參數(shù)區(qū)分,調(diào)用的時(shí)基類(lèi)的函數(shù)
return 0;
}
3.成員函數(shù)重寫(xiě)
(1)什么是成員函數(shù)的重寫(xiě)
派生類(lèi)中函數(shù)簽名與基類(lèi)函數(shù)簽名完全相同的情況也被稱(chēng)為重寫(xiě)。
注意區(qū)分重寫(xiě)和重載,
重載:在通過(guò)一個(gè)命名空間的,比如同一個(gè)類(lèi)成員函數(shù)重載,或者外部函數(shù)
重載。
重寫(xiě):重寫(xiě)對(duì)于多態(tài)機(jī)制的實(shí)現(xiàn)非常的重要,c++中多態(tài)的實(shí)現(xiàn)靠的就是子類(lèi)對(duì)
父類(lèi)虛函數(shù)的重寫(xiě)實(shí)現(xiàn)的。
(2.普通函數(shù)的重寫(xiě)與靜態(tài)綁定
(1)靜態(tài)綁定的規(guī)則
調(diào)用某個(gè)對(duì)象的函數(shù)時(shí),只會(huì)調(diào)用該對(duì)象內(nèi)實(shí)現(xiàn)該函數(shù),即便該函數(shù)在派生類(lèi)中有被
重寫(xiě),但它不會(huì)調(diào)用該函數(shù)在派生類(lèi)中的重寫(xiě)版本。
(2)靜態(tài)綁定由誰(shuí)決定
靜態(tài)綁定是由編譯器決定的,靜態(tài)綁定后,這種綁定關(guān)系不可以在程序運(yùn)行時(shí)被改變。
(3)什么情況下遵守靜態(tài)規(guī)則
對(duì)普通函數(shù)進(jìn)行重寫(xiě)時(shí),函數(shù)的調(diào)用遵守靜態(tài)規(guī)則,這一點(diǎn)與java不一樣,java對(duì)普通
函數(shù)的重寫(xiě)是動(dòng)態(tài)綁定。
注意:實(shí)際上對(duì)普通函數(shù)進(jìn)行重寫(xiě)并沒(méi)有太大的實(shí)際意義。
(4)靜態(tài)綁定的例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <string>
#include <cassert>
#include <errno.h>
using namespace std;
class Box
{
protected:
int length;
int width;
int height;
public:
Box(int length, int width, int height)
:length(length),width(width),height(height) { }
int volume() {return length*width*height;}
void showvolume() {cout<<"體積:"<<volume()<<endl;}
};
class Thinbox : public Box {
public:
Thinbox(int length, int width, int height):Box(length, width, height) { }
int volume() {return length*width*height*(1-0.2);}
//void showvolume() {cout<<"體積:"<<volume()<<endl;}打開(kāi))
};
int main(void)
{
Thinbox tbox(3, 3, 3);
Box *boxp = &tbox;
tbox.showvolume();
boxp->showvolume();
return 0;
}
運(yùn)行結(jié)果:
體積:27
體積:27
例子分析:
例子中,定義了一個(gè)Box類(lèi),類(lèi)中實(shí)現(xiàn)了兩個(gè)函數(shù),一個(gè)用于計(jì)算體積的
volume函數(shù),該函數(shù)計(jì)算體積時(shí),沒(méi)有考慮盒子壁的厚度。
另一個(gè)是用于顯示體積的函數(shù),
void showvolume() {cout<<"體積:"<<volume()<<endl;}
從Box中派生了一個(gè)Thinbox類(lèi),在這個(gè)類(lèi)中并沒(méi)有新增任何的成員,只是
重寫(xiě)了基類(lèi)的volume()函數(shù),
int volume() {return length*width*height*(1-0.2);}
考慮到盒子的厚度,因此體積*(1-0.2),進(jìn)行了體積估算。
但是沒(méi)有重寫(xiě)showvolume()函數(shù),所以通過(guò)子類(lèi)調(diào)用的showvolume()函數(shù)是
從基類(lèi)繼承而來(lái)的showvolume函數(shù)。
在主函數(shù)中,定義了一個(gè)派生類(lèi)Thinbox的對(duì)象tbox,然后在再定義了一個(gè)
基類(lèi)Box類(lèi)型的指針boxp,該指針指向派生類(lèi)tbox。
面函數(shù)中通過(guò)如下兩條語(yǔ)句顯示體積。
tbox.showvolume();
box->showvolume();
通過(guò)tbox調(diào)用showvolume()函數(shù)時(shí),因?yàn)榕缮?lèi)中沒(méi)有該函數(shù),就進(jìn)入派生類(lèi)
對(duì)象中的基類(lèi)對(duì)象中調(diào)用基類(lèi)的該函數(shù)(繼承過(guò)來(lái)的函數(shù)),基類(lèi)該函數(shù)在調(diào)
用volume函數(shù)時(shí),發(fā)現(xiàn)在基類(lèi)中存在,根據(jù)靜態(tài)綁定規(guī)則,不管這個(gè)函數(shù)有沒(méi)
有重寫(xiě),調(diào)用的是這個(gè)函數(shù)本身,而不是它在派生類(lèi)中的重寫(xiě)函數(shù)。
boxp->showvolume()是通過(guò)基類(lèi)指針調(diào)用基類(lèi)showvolume()函數(shù),由于boxp是
基類(lèi)類(lèi)型的指針,所以直接進(jìn)入派生類(lèi)的基類(lèi)對(duì)象中調(diào)用showvolume()函數(shù),
該函數(shù)在調(diào)用volume函數(shù),過(guò)程同樣遵循靜態(tài)綁定規(guī)則。
從如下顯示結(jié)果來(lái)看:
體積:27
體積:27
說(shuō)明派生類(lèi)的volume()函數(shù)并沒(méi)有被調(diào)用,因?yàn)闆](méi)有*(1-0.2)進(jìn)行體積縮減。
如果我們?cè)谂缮?lèi)中將派生類(lèi)中將派生類(lèi)的如下顯示函數(shù)打開(kāi),
void showvolume() {cout<<"體積:"<<volume()<<endl;}打開(kāi))
然后再次編譯運(yùn)行,結(jié)果如下
體積:21
體積:27
在tbox.showvolume();調(diào)用時(shí),首先在當(dāng)前類(lèi)對(duì)象查找,由于在派生類(lèi)對(duì)象tbox
中有定義showvolume();函數(shù),會(huì)根據(jù)靜態(tài)規(guī)則調(diào)用該函數(shù),以及涉及到的volume函數(shù)。
4.虛函數(shù)的重寫(xiě)和動(dòng)態(tài)綁定(多態(tài))
(1)動(dòng)態(tài)綁定的規(guī)則
調(diào)用某對(duì)象中的函數(shù)時(shí),如果函數(shù)在派生類(lèi)中被重寫(xiě)了的話(huà),那么優(yōu)先調(diào)用派生類(lèi)中
被重寫(xiě)的函數(shù),否則才會(huì)調(diào)用自己定義的函數(shù)。
(2)如何才能實(shí)現(xiàn)動(dòng)態(tài)綁定
對(duì)虛函數(shù)進(jìn)行重寫(xiě),只有虛函數(shù)才能實(shí)現(xiàn)動(dòng)態(tài)綁定。
(3)靜態(tài)綁定與動(dòng)態(tài)綁定對(duì)比
(1)靜態(tài)綁定先到當(dāng)前類(lèi)對(duì)象和基類(lèi)對(duì)象中尋找
(2)動(dòng)態(tài)綁定與靜態(tài)綁定相反,先到當(dāng)前類(lèi)的派生類(lèi)對(duì)象中尋找,找不到才使用當(dāng)前類(lèi)和基類(lèi)中的函數(shù)
(3)重寫(xiě)普通函數(shù)時(shí)遵守的都是靜態(tài)綁定規(guī)則,重寫(xiě)虛函數(shù)時(shí)遵守的都是動(dòng)態(tài)綁定規(guī)則。
(4)虛函數(shù)的聲明格式
virtual 返回值 fun(...) { }
注意:
(1)如果函數(shù)的定義和聲明是分開(kāi)的,那么virtual應(yīng)該放在函數(shù)的聲明前。
(2)派生類(lèi)中對(duì)基類(lèi)虛函數(shù)進(jìn)行重寫(xiě)后,這個(gè)重寫(xiě)函數(shù)仍然還是一個(gè)虛函數(shù),不管有沒(méi)有顯式表明virtual關(guān)鍵字。
(3)在派生類(lèi)中,為了讓語(yǔ)義更加清晰,應(yīng)該在重寫(xiě)的虛函數(shù)前面加virtual關(guān)鍵字
(4)如果基類(lèi)中的虛函數(shù)是const,那么子類(lèi)中重寫(xiě)的函數(shù)必須也是const
(5)動(dòng)態(tài)綁定舉例子
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <string>
#include <cassert>
#include <errno.h>
using namespace std;
class Box
{
protected:
int length;
int width;
int height;
public:
Box(int length, int width, int height)
:length(length),width(width),height(height) { }
virtual int volume() {return length*width*height;}
virtual void showvolume() {cout<<"體積:"<<volume()<<endl;}
};
class Thinbox : public Box {
public:
Thinbox(int length, int width, int height):Box(length, width, height) { }
int volume() {return length*width*height*(1-0.2);}
};
int main(void)
{
Thinbox tbox(3, 3, 3);
tbox.showvolume();
return 0;
}
運(yùn)行結(jié)果:
體積:21
例子分析:
在例子中,通過(guò)派生類(lèi)對(duì)象tbox調(diào)用showvolume();函數(shù)時(shí),由于該函數(shù)在派生類(lèi)中并
沒(méi)有被重寫(xiě),所以調(diào)用的實(shí)際上時(shí)基類(lèi)的showvolume(),基類(lèi)的showvolume()調(diào)用
volume函數(shù)計(jì)算體積時(shí),由于該函數(shù)時(shí)虛函數(shù),并且該函數(shù)被派生類(lèi)重寫(xiě)了,根據(jù)動(dòng)
態(tài)綁定的規(guī)則,需要優(yōu)先調(diào)用被派生類(lèi)中重寫(xiě)的函數(shù),因此結(jié)果是21。
5. 動(dòng)態(tài)綁定和多態(tài)
(1)向上轉(zhuǎn)型和向下轉(zhuǎn)型
有了動(dòng)態(tài)綁定,距離多態(tài)的實(shí)現(xiàn)也就只有一步之遙,如果需要利用動(dòng)態(tài)綁定實(shí)現(xiàn)多態(tài),我
們還需要加入向上轉(zhuǎn)型的操作。所謂向上轉(zhuǎn)型,就是使用基類(lèi)的指針或者引用指向派生類(lèi)
對(duì)象。利用基類(lèi)指針調(diào)用派生類(lèi)中重寫(xiě)的虛函數(shù)時(shí),實(shí)現(xiàn)的就是多態(tài)的調(diào)用。
(1)向上轉(zhuǎn)型舉例
向上轉(zhuǎn)型例子:
Thinbox tbox(3, 3, 3);
Box *boxp = &tbox;
向上轉(zhuǎn)型其實(shí)就是類(lèi)型的強(qiáng)制轉(zhuǎn)換,只是向上轉(zhuǎn)型都是自動(dòng)進(jìn)行的,不需要進(jìn)行
顯式的強(qiáng)制轉(zhuǎn)換。
(3)轉(zhuǎn)型涉及的靜態(tài)類(lèi)型和動(dòng)態(tài)類(lèi)型
在向上轉(zhuǎn)型的過(guò)程中,=兩邊涉及兩種類(lèi)型,=左邊的類(lèi)型被稱(chēng)為靜態(tài)類(lèi)型,
右邊的類(lèi)型稱(chēng)為動(dòng)態(tài)類(lèi)型,動(dòng)態(tài)的意思表示對(duì)象的類(lèi)型不定,可以與左邊類(lèi)型
相同,也可以是左邊類(lèi)型的任何一個(gè)派生類(lèi)型的對(duì)象。
(4)向下轉(zhuǎn)型
有向上轉(zhuǎn)型就有向下轉(zhuǎn)型,但是向上轉(zhuǎn)型時(shí)是無(wú)條件成立的,因?yàn)槎x派生類(lèi)對(duì)象
時(shí)一定會(huì)實(shí)例化其基類(lèi)對(duì)象,但是向下轉(zhuǎn)型不一定會(huì)成功,只有向上轉(zhuǎn)型后的指針
或者引用才能向下轉(zhuǎn)型,因?yàn)檫@樣才能保證派生類(lèi)對(duì)象相對(duì)于基類(lèi)多出的空間一定存在。
向上轉(zhuǎn)型時(shí)大空間向小空間轉(zhuǎn)換,而向下轉(zhuǎn)型時(shí)小空間向大空間轉(zhuǎn)換,所有向下轉(zhuǎn)型
時(shí)要格外小心。向下轉(zhuǎn)型時(shí)必須顯式的進(jìn)行類(lèi)型的強(qiáng)制轉(zhuǎn)換。
向下轉(zhuǎn)型舉例:
(1)錯(cuò)誤例子
Box box(3, 3, 3);
Box *boxp= &box;
Thinbox *tboxp = (Thinbox *)boxp;
(2)正確例子
Thinbox tbox(3, 3, 3);
Box *boxp= &box;
Thinbox *tboxp = (Thinbox *)boxp;
(2)多態(tài)舉例
多態(tài)只有兩種形式,一種是通過(guò)指針?lè)绞綄?shí)現(xiàn),另一種是通過(guò)引用實(shí)現(xiàn)。
(1)指針實(shí)現(xiàn)初始化
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <string>
#include <cassert>
#include <errno.h>
using namespace std;
/* 普通箱子 */
class Box
{
protected:
int length;
int width;
int height;
public:
Box(int length, int width, int height)
:length(length),width(width),height(height) { }
/* 虛函數(shù) */
virtual int volume() const {return length*width*height;}
};
/* 薄箱子 */
class Thinbox : public Box {
public:
Thinbox(int length, int width, int height):Box(length, width, height) { }
/* 由于基類(lèi)的volume函數(shù)是虛汗數(shù),就算這里不知定virtual,也是虛函數(shù) */
virtual int volume() const {return length*width*height*(1-0.2);}
};
/* 厚箱子 */
class Thickbox : public Box {
public:
Thickbox(int length, int width, int height):Box(length, width, height) { }
virtual int volume() const {return length*width*height*(1-0.5);}
};
int main(void)
{
Thinbox thinbox(3, 3, 3);
Thickbox thickbox(3, 3, 3);
Box *boxp = NULL;
cout <<"選則顯示那個(gè)箱子的體積"<<endl;
cout <<"1.薄箱子"<<endl;
cout <<"2.厚箱子"<<endl;
cout <<"選擇:";
int select = 0;
cin >> select;
if(1 == select) boxp = &thinbox;
else if(2 == select) boxp = &thickbox;
cout <<"箱子體積:"<< boxp->volume() << endl;
return 0;
}
運(yùn)行結(jié)果:
運(yùn)行時(shí)提示,選擇顯示社么箱子的體積,選擇1,表示顯示薄箱子的體積,
如果選擇2,表示選擇厚箱子的體積,選擇1,運(yùn)行結(jié)果如下。
選則顯示那個(gè)箱子的體積
1.薄箱子
2.厚箱子
選擇:1
箱子體積:271
例子分析:
main函數(shù)中定義了一個(gè)Box基類(lèi)的指針,根據(jù)不同選擇,基類(lèi)指針?lè)謩e指向不同
派生類(lèi)對(duì)象,通過(guò)基類(lèi)調(diào)用volume函數(shù)時(shí),根據(jù)情況的不同,實(shí)際上調(diào)用的是
不同派生類(lèi)對(duì)象的volume函數(shù),這就是多態(tài)。
(2)引用實(shí)現(xiàn)多態(tài)
還是上面的例子,但是將main函數(shù)改為如下形式,并增加一個(gè)子函數(shù)。
void showvolume(const Box &box) {
cout <<"箱子體積:"<< box.volume() << endl;
}
int main(void)
{
Thinbox thinbox(3, 3, 3);
Thickbox thickbox(3, 3, 3);
cout <<"選則顯示那個(gè)箱子的體積"<<endl;
cout <<"1.薄箱子"<<endl;
cout <<"2.厚箱子"<<endl;
int select = 0;
cin >> select;
if(1 == select) showvolume(thinbox);
else if(2 == select) showvolume(thickbox);
return 0;
}
例子分析:
引用只能初始化,不能賦值,這一點(diǎn)顯然與指針不同,因此通過(guò)引用實(shí)現(xiàn)多態(tài),
通常都是通過(guò)函數(shù)形參實(shí)現(xiàn)的,多次調(diào)用該函數(shù),可以多次對(duì)引用類(lèi)型的形參
進(jìn)行初始化。
(3)虛函數(shù)指針表vtable
之所以能夠?qū)崿F(xiàn)多態(tài),是因?yàn)閷?duì)象中會(huì)維護(hù)一張?zhí)摵瘮?shù)指針表vtable,當(dāng)使用基
類(lèi)被重寫(xiě)的接口調(diào)用虛函數(shù)時(shí),會(huì)到表中查找需要?jiǎng)討B(tài)綁定調(diào)用的重寫(xiě)函數(shù),這
個(gè)實(shí)現(xiàn)機(jī)制會(huì)使實(shí)現(xiàn)多態(tài)的對(duì)象多消耗幾個(gè)字節(jié)來(lái)存放虛函數(shù)表。但是對(duì)于多態(tài)
帶來(lái)的好處來(lái)說(shuō),多消耗幾個(gè)字節(jié)資源算不上什么。
因此,如果我們的多態(tài)實(shí)現(xiàn)有錯(cuò)誤時(shí),編譯時(shí)往往可能會(huì)提示vtable表有問(wèn)題。
6. 純虛函數(shù)
(1)什么是純虛函數(shù)
只有虛函數(shù)的聲明,沒(méi)有函數(shù)定義的就是純虛函數(shù)。
純虛函數(shù)的格式:virtual 返回值 函數(shù)名(參數(shù)列表) = 0;
如果函數(shù)是const類(lèi)型的函數(shù),=0需要放在const的后面,定義純虛函數(shù)時(shí),必寫(xiě)=0的標(biāo)識(shí)。
(2)為什么需要純虛函數(shù)
很多時(shí)候,基類(lèi)中的虛函數(shù)只需要留下一個(gè)接口即可,不需要具體實(shí)現(xiàn),因?yàn)樘摵瘮?shù)的具
體實(shí)現(xiàn)應(yīng)該留給派生類(lèi)的重寫(xiě)函數(shù)來(lái)完成。
在java中普通函數(shù)就可以實(shí)現(xiàn)多態(tài)的,但是在c++中,只有虛函數(shù)才能實(shí)現(xiàn)多態(tài),C++中的
純虛函數(shù)就是java中的抽象函數(shù)。c++和java的實(shí)現(xiàn)多態(tài)時(shí),函數(shù)重寫(xiě)的的對(duì)應(yīng)關(guān)系是這樣的。
c++中的虛函數(shù)重寫(xiě) <===對(duì)應(yīng)===> java中普通函數(shù)的重寫(xiě)
c++中的純虛函數(shù)重寫(xiě) <===對(duì)應(yīng)===> java中抽象函數(shù)的重寫(xiě)
比如完全可以將前面多態(tài)例子中的Box基類(lèi)中的volume()函數(shù)設(shè)置純虛函數(shù)。
virtual int volume() const = 0;
程序運(yùn)行的結(jié)果與之前是一樣。
(3)抽象類(lèi)
(1)什么是抽象類(lèi)
只要有包含純虛函數(shù)的類(lèi)就是抽象類(lèi)。
(2)抽象類(lèi)的作用
抽象類(lèi)用于給定函數(shù)接口,搭建程序框架,所有的函數(shù)的具體實(shí)現(xiàn)全部由派生類(lèi)重寫(xiě)
實(shí)現(xiàn)。抽象類(lèi)固定了接口,但是代碼的實(shí)現(xiàn)卻很靈活,都掌握在派生類(lèi)手中。
(3)抽象類(lèi)特點(diǎn)
(1)不能實(shí)例化對(duì)象,只能通過(guò)派生類(lèi)實(shí)例化對(duì)象
(2)如果派生類(lèi)不全部實(shí)現(xiàn)抽象基類(lèi)的純虛函數(shù)的話(huà),派生類(lèi)也是一個(gè)抽象類(lèi),
被實(shí)現(xiàn)了的純虛函數(shù)就變?yōu)榱似胀ㄌ摵瘮?shù)。
如果在子類(lèi)中繼續(xù)將函數(shù)保持為純虛函數(shù)的話(huà),=0標(biāo)志可以不用設(shè)置。
(3)抽象類(lèi)雖然不能直接實(shí)例化,但是有自己構(gòu)造函數(shù),因?yàn)橥ㄟ^(guò)派生類(lèi)間接實(shí)現(xiàn)
抽象類(lèi)實(shí)例化時(shí),需要在派生類(lèi)的初始化列表中調(diào)用抽象類(lèi)的構(gòu)造函數(shù)初始
化其成員。
抽象類(lèi)的構(gòu)造函數(shù)不能是純虛函數(shù),通過(guò)派生類(lèi)對(duì)象實(shí)例化抽象基類(lèi)時(shí),沒(méi)
有辦法調(diào)用虛構(gòu)造函數(shù)初始化抽象類(lèi)的成員,這會(huì)導(dǎo)致不確定的結(jié)果。
(4)由于抽象類(lèi)不能創(chuàng)建對(duì)象,因此不能把抽象類(lèi)作為參數(shù)類(lèi)型和返回類(lèi)型,因?yàn)? 作為參數(shù)和返回類(lèi)型時(shí)都需要開(kāi)辟對(duì)象空間,但是抽象類(lèi)并不允許被實(shí)例化。
(5)由于抽象類(lèi)不會(huì)直接實(shí)例化,因此抽象類(lèi)的構(gòu)造函數(shù)通常都是由子類(lèi)初始化
列表調(diào)用,外部不會(huì)調(diào)用,因此通常做法是將抽象類(lèi)的構(gòu)造函數(shù)設(shè)置為
protected。
7.虛析構(gòu)函數(shù)
(1)通過(guò)基類(lèi)指針釋放派生類(lèi)對(duì)像時(shí)遇到的問(wèn)題
常常會(huì)使用基類(lèi)指針指向動(dòng)態(tài)分配的派生類(lèi)對(duì)象,當(dāng)我們通過(guò)基類(lèi)指針釋放派生類(lèi)的空間
時(shí),往往會(huì)出現(xiàn)問(wèn)題,比如還是利用前面Box的例子來(lái)說(shuō)明這個(gè)問(wèn)題。
例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <string>
#include <cassert>
#include <errno.h>
using namespace std;
/* 普通箱子 */
class Box
{
protected:
int length;
int width;
int height;
public:
Box(int length, int width, int height)
:length(length),width(width),height(height) { }
~Box() {
cout<<"Box' destructor"<<endl;
}
/* 虛函數(shù) */
virtual int volume() const = 0;
virtual int volume1() const { }
};
/* 薄箱子 */
class Thinbox : public Box {
public:
Thinbox(int length, int width, int height):Box(length, width, height) { }
~Thinbox() {
cout<<"thinbox' destructor"<<endl;
}
/* 由于幾類(lèi)的volume函數(shù)是虛汗數(shù),就算這里不知定virtual,也是虛函數(shù) */
virtual int volume() const {return length*width*height*(1-0.2);}
};
/* 厚箱子 */
class Thickbox : public Box {
public:
Thickbox(int length, int width, int height):Box(length, width, height) { }
~Thickbox() {
cout<<"Thickbox' destructor"<<endl;
}
virtual int volume() const {return length*width*height*(1-0.5);}
};
int main(void)
{
Box *boxp[] = {new Thinbox(3,3,3), new Thickbox(3,3,3)};
for(int i=0; i<sizeof(boxp)/sizeof(boxp[0]); i++) {
delete(boxp[i]);
}
return 0;
}
運(yùn)行結(jié)果:
Box' destructor
Box' destructor
例子分析:
例子中分別在基類(lèi)和派生類(lèi)中加入了各自的析構(gòu)函數(shù),主函數(shù)中定義了一個(gè)
基類(lèi)指針數(shù)組,元素分別指向派生類(lèi)對(duì)象,通過(guò)基類(lèi)指針釋放派生類(lèi)對(duì)象時(shí),
希望調(diào)用派生類(lèi)各自的析構(gòu)函數(shù),然后再調(diào)用基類(lèi)的析構(gòu)函數(shù)。
顯然從結(jié)果看,沒(méi)有達(dá)到我們想要的結(jié)果,因?yàn)椴](méi)有調(diào)用派生類(lèi)的析構(gòu)函數(shù)。
(2)虛析構(gòu)函數(shù)
通過(guò)基類(lèi)指針釋放派生類(lèi)對(duì)象時(shí),如果希望調(diào)用派生類(lèi)析構(gòu)函數(shù)的話(huà),就應(yīng)該使用虛析
構(gòu)函數(shù)來(lái)解決這個(gè)問(wèn)題。
使用虛析構(gòu)函數(shù)時(shí),應(yīng)該將基類(lèi)的析構(gòu)函數(shù)設(shè)置為虛析構(gòu)函數(shù),設(shè)置派生類(lèi)的虛析構(gòu)函
數(shù)并不起作用。
如果在上面例子中基類(lèi)的虛構(gòu)函數(shù)前面添加virtual關(guān)鍵字即可。
virtual ~Box() {
cout<<"Box' destructor"<<endl;
}
再次編譯運(yùn)行,就會(huì)得到如下正確結(jié)果:
thinbox' destructor
Box' destructor
Thickbox' destructor
Box' destructor
(3)什么時(shí)候使用虛析構(gòu)函數(shù)
使用繼承時(shí),并且類(lèi)中至少包含一個(gè)虛函數(shù)時(shí),我們應(yīng)該總是把基類(lèi)的析構(gòu)函數(shù)聲明
為虛析構(gòu)函數(shù),以保證析構(gòu)函數(shù)被正確調(diào)用,實(shí)現(xiàn)資源的正確釋放。
8. typeid操作符
(1)作用有兩個(gè)
(1)確認(rèn)類(lèi)型
(2)類(lèi)型統(tǒng)一
typeid()操作符相當(dāng)于java中的instanceof()操作符。
(2)typeid操作符詳解
(1)使用格式
typeid(expression)
expression可以是:
(1)類(lèi)型,
(1)基本類(lèi)或者基本類(lèi)型相關(guān)的指針和引用類(lèi)型
(2)組合類(lèi)型,數(shù)組,結(jié)構(gòu)體,聯(lián)合體,類(lèi)類(lèi)型,以及相關(guān)的指針引用
比如:
typeid(int),typeid(Student)
(3)具體對(duì)象
具體數(shù)值,結(jié)構(gòu)體/聯(lián)合體/變量,類(lèi)對(duì)象
比如:
int a;
typeid(a);
或者
type(10);
(2)操作符返回類(lèi)型
使用typeid操作符,返回的是type_info類(lèi)類(lèi)型的引用,
(1)該類(lèi)類(lèi)型特點(diǎn)
(1)實(shí)現(xiàn)了虛析構(gòu)函數(shù),可以作為基類(lèi)安全使用
(2)它的構(gòu)造函數(shù),副本構(gòu)造函數(shù),=重載函數(shù)都被private限制因此不
能直接使用該類(lèi)型實(shí)例化對(duì)象,不能初始化,不能進(jìn)行對(duì)象的賦值運(yùn)算。
(3)使用typeid操作符是唯一產(chǎn)生type_info類(lèi)型對(duì)象的方法。
(2)該類(lèi)型實(shí)現(xiàn)了的運(yùn)算符
(1)t1==t2:類(lèi)型相同表達(dá)式為true,否者false
(2)t1!=t2:類(lèi)型不相同表達(dá)式為true,否者false
(3)t.name():返回類(lèi)型名稱(chēng)
(4)例子
int main(void)
{
int a = 10;
int b = 10;
long c = 120;
if(typeid(a)==typeid(b)) {
cout<<"a 與 b的類(lèi)型相同\n"<<endl;
}
if(typeid(c)!=typeid(23.7)) {
cout<<"c 與 23.7的類(lèi)型相同\n"<<endl;
}
if(typeid(c)==typeid(long)) {
cout<<"c是long型\n"<<endl;
}
int *p = &a;
cout <<"p的類(lèi)型"<<typeid(p).name() << endl<<endl;
cout <<"*p的類(lèi)型"<<typeid(*p).name() << endl<<endl;
cout <<"*p的類(lèi)型"<<typeid(*p).name() << endl<<endl;
return 0;
}
以上測(cè)試方式對(duì)于結(jié)構(gòu)體,數(shù)組,聯(lián)合體等組合類(lèi)型同樣有效。
(3)靜態(tài)類(lèi)型確認(rèn)
靜態(tài)類(lèi)型與sizeof同,但是sizeof不能提供==/!=等操作,靜態(tài)類(lèi)型的確認(rèn)是由編譯器
在編譯時(shí)進(jìn)行計(jì)算的。
(1)哪些情況是靜態(tài)類(lèi)型確認(rèn)
(1)對(duì)于非類(lèi)類(lèi)型的類(lèi)型確認(rèn),以及不包含虛函數(shù)的類(lèi)對(duì)象的類(lèi)型確認(rèn),
都是靜態(tài)類(lèi)型確認(rèn)。
(2)如果typeid參數(shù)是類(lèi)型的時(shí)候,比如
typeid(int)
或者
typeid(Box)
進(jìn)行的都是靜態(tài)類(lèi)型確認(rèn)
(3)動(dòng)態(tài)類(lèi)型確認(rèn)RTTI(run-time Type identification)
(1)什么時(shí)候進(jìn)行動(dòng)態(tài)類(lèi)型確認(rèn),什么是動(dòng)態(tài)類(lèi)型確認(rèn)
對(duì)指向派生類(lèi)對(duì)象的包含有虛函數(shù)的基類(lèi)指針解引用和引用使用typeid進(jìn)行
類(lèi)型確認(rèn)時(shí),得到的是派生類(lèi)的類(lèi)型,這就是動(dòng)態(tài)類(lèi)型確認(rèn)。
注意:
(1)這里強(qiáng)調(diào)的是基類(lèi)指針解釋引用(*p)和引用,如果直接對(duì)基類(lèi)類(lèi)型,指
針或者對(duì)象進(jìn)行typeid時(shí),是靜態(tài)類(lèi)型確認(rèn)。
(2)引用必須初始化,但是指針可以為NULL,如果動(dòng)態(tài)確認(rèn)時(shí),基類(lèi)指針為
NULL,會(huì)導(dǎo)致typeid動(dòng)態(tài)類(lèi)型確認(rèn)失敗。
例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <string>
#include <cassert>
#include <errno.h>
#include <typeinfo>
using namespace std;
class Box
{
protected:
int length;
int width;
int height;
public:
Box(int length, int width, int height)
:length(length),width(width),height(height) { }
virtual ~Box() { }
/* 虛函數(shù) */
virtual int volume() const {}
};
/* 薄箱子 */
class Thinbox : public Box {
public:
Thinbox(int length, int width, int height):Box(length, width, height) { }
/* 由于幾類(lèi)的volume函數(shù)是虛汗數(shù),就算這里不知定virtual,也是虛函數(shù) */
virtual int volume() const {return length*width*height*(1-0.2);}
};
int main(void)
{
Thinbox thinbox(1,2,3);
Box box1(1,2,3);
Box &box2 = thinbox;
Box *boxp = NULL;
cout << "對(duì)類(lèi)型直接typeid是靜態(tài)類(lèi)型確認(rèn)" <<endl;
cout << "typeid判斷基類(lèi)類(lèi)型Box的類(lèi)型為:" <<typeid(Box).name() << endl;
cout << "typeid判斷派生類(lèi)類(lèi)型Thinbox的類(lèi)型為:" <<typeid(thinbox).name() << endl;
cout << "\n對(duì)類(lèi)對(duì)象或者指針typeid也是靜態(tài)類(lèi)型確認(rèn)" <<endl;
cout << "typeid判斷對(duì)象thinbox的類(lèi)型為:" <<typeid(thinbox).name() << endl;
cout << "typeid判斷對(duì)象box1的類(lèi)型為:" <<typeid(box1).name() << endl;
cout << "typeid判斷基類(lèi)指針boxp的類(lèi)型為:" <<typeid(boxp).name() << endl;
cout << "\n對(duì)指向派生類(lèi)對(duì)象的包含有虛函數(shù)的基類(lèi)";
cout <<"指針解引用和引用使用typeid時(shí)是動(dòng)態(tài)類(lèi)型確認(rèn)" <<endl;
cout << "typeid判斷基類(lèi)引用box2的類(lèi)型為:" <<typeid(box2).name() << endl;
cout << "typeid判斷基類(lèi)指針*boxp的類(lèi)型為:" <<typeid(*boxp).name() << endl;
return 0;
}
(2)動(dòng)態(tài)確認(rèn)的好處
使用動(dòng)態(tài)類(lèi)型,便于在程序中根據(jù)類(lèi)型的不同進(jìn)行選擇不同的分支運(yùn)行。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <string>
#include <cassert>
#include <errno.h>
#include <typeinfo>
using namespace std;
/* 普通箱子 */
class Box
{
protected:
int length;
int width;
int height;
public:
Box(int length, int width, int height)
:length(length),width(width),height(height) { }
virtual ~Box() { }
/* 虛函數(shù) */
virtual int volume() const {}
};
/* 薄箱子 */
class Thinbox : public Box {
public:
Thinbox(int length, int width, int height):Box(length, width, height) { }
/* 由于幾類(lèi)的volume函數(shù)是虛汗數(shù),就算這里不知定virtual,也是虛函數(shù) */
virtual int volume() const {return length*width*height*(1-0.2);}
};
/* 厚箱子 */
class Thickbox : public Box {
public:
Thickbox(int length, int width, int height):Box(length, width, height) { }
virtual int volume() const {return length*width*height*(1-0.5);}
};
//void using_box(const Box *box) {
void using_box(const Box &box) {
cout <<"箱子體積:"<<box.volume()<<endl;
//if(typeid(*box)==typeid(Thinbox)) {//使用指針時(shí),需要解引用
if(typeid(box)==typeid(Thinbox)) {
cout<<"薄箱子,不結(jié)實(shí),用來(lái)裝衣物!"<<endl;
//} else if(typeid(*box)==typeid(Thickbox)) {
} else if(typeid(box)==typeid(Thickbox)) {
cout<<"厚箱子,很結(jié)實(shí),用來(lái)裝健身用品!"<<endl;
}
}
int main(void)
{
Thinbox thinbox(1,2,3);
Thickbox thickbox(1,2,3);
//using_box(&thinbox);
using_box(thinbox);
printf("\n");
//using_box(&thickbox);
using_box(thickbox);
return 0;
}
運(yùn)行結(jié)果:
箱子體積:4
薄箱子,不結(jié)實(shí),用來(lái)裝衣物!
箱子體積:3
厚箱子,很結(jié)實(shí),用來(lái)裝健身用品!
例子分析:
在例子中的using_box函數(shù)中,
void using_box(const Box &box) {
cout <<"箱子體積:"<<box.volume()<<endl;
if(typeid(box)==typeid(Thinbox)) {
cout<<"薄箱子,不結(jié)實(shí),用來(lái)裝衣物!"<<endl;
} else if(typeid(box)==typeid(Thickbox)) {
cout<<"厚箱子,不結(jié)實(shí),用來(lái)裝健身用品!"<<endl;
}
}
對(duì)類(lèi)型進(jìn)行了動(dòng)態(tài)確認(rèn),根據(jù)基類(lèi)引用box指向的不同的派生類(lèi)類(lèi)型
,通過(guò)分支語(yǔ)句選擇做不同的事情。
從這里我們看出,使用基類(lèi)引用或者指針有一個(gè)好處就是可以用于
統(tǒng)一各種不同的派生類(lèi)型對(duì)象的傳參。
(3)動(dòng)態(tài)類(lèi)型確認(rèn)帶來(lái)的問(wèn)題
動(dòng)態(tài)類(lèi)型確認(rèn)有它一定的用途,但是動(dòng)態(tài)類(lèi)型確認(rèn)也帶了一個(gè)問(wèn)題,那就是
不利于解耦。
我們之所以使用多態(tài),是為了實(shí)現(xiàn)分層結(jié)構(gòu)的程序開(kāi)發(fā),層與層之間應(yīng)該盡
可能的減少耦合。
但是在上面使用動(dòng)態(tài)類(lèi)型確認(rèn)的例子中,using_box函數(shù)涉及的的耦合性太強(qiáng),
因?yàn)樵摵瘮?shù)里面使用到了具體的派生類(lèi)型,如果派生類(lèi)型發(fā)生改變,using_box
函數(shù)就必須相應(yīng)改動(dòng),顯然耦合性太強(qiáng)。
在實(shí)際開(kāi)發(fā)中,應(yīng)該盡量利用多態(tài)的特點(diǎn),在上層使用基類(lèi)提供的統(tǒng)一接口調(diào)用
下層派生類(lèi)中重寫(xiě)的函數(shù),降低層級(jí)間的耦合。
9. 使用多態(tài)降低耦合
例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <string>
#include <cassert>
#include <errno.h>
#include <typeinfo>
using namespace std;
class Plane {
public:
Plane(const string name): name(name) {}
virtual ~Plane() {}
virtual void turn_left(int agree) const=0;
virtual void turn_right(int agree) const=0;
virtual void go_forward(int speed) const=0;
protected:
string name;
};
class Byplane: public Plane {
public:
Byplane(const string name="by747"):Plane(name) {}
void turn_left(int agree) const {
cout<<name<<" turn_left "<<agree<<"度"<<endl;
}
void turn_right(int agree) const {
cout<<name<<" turn_right "<<agree<<"度"<<endl;
}
void go_forward(int speed) const {
cout<<name<<" go forward "<<speed<<"英里"<<endl;
}
};
class Airplane: public Plane {
public:
Airplane(const string name="air380"):Plane(name) {}
virtual ~Airplane() {}
void turn_left(int agree) const {
cout<<name<<" turn_left "<<agree<<"度"<<endl;
}
void turn_right(int agree) const {
cout<<name<<" turn_right "<<agree<<"度"<<endl;
}
void go_forward(int speed) const {
cout<<name<<" go forward "<<speed<<"英里"<<endl;
}
};
void oprate_plane(const Plane &plane) {
cout << "飛機(jī)操作選擇" << endl;
cout << "1.左轉(zhuǎn)" << endl;
cout << "2.右轉(zhuǎn)" << endl;
cout << "3.前進(jìn)" << endl;
int select = 0;
cin>>select;
int tmp = 0;
if(1 == select) {
cout << "輸入轉(zhuǎn)向角度數(shù)" << endl;
cin>>tmp;
plane.turn_left(tmp);
} else if(2 == select) {
cout << "輸入轉(zhuǎn)向角度數(shù)" << endl;
cin>>tmp;
plane.turn_right(tmp);
} else if(3 == select) {
cout << "輸入前進(jìn)速度" << endl;
cin>>tmp;
plane.go_forward(tmp);
}
}
int main(void)
{
Byplane byplane("by747");
Airplane airplane("air380");
oprate_plane(byplane);
return 0;
}
例子分析:
在operate_plane函數(shù)中,所有的操作只與基類(lèi)相關(guān),與派生類(lèi)沒(méi)有任何直接關(guān)系,
就算是開(kāi)發(fā)出新的派生類(lèi),只要遵守plane基類(lèi)規(guī)定的接口,就可以直接使用
operate_plane函數(shù)操作,無(wú)需任何更改,完全解除了operate_plane函數(shù)與派生類(lèi)
之間的耦合。
10. 類(lèi)成員指針
(1)通過(guò)指針訪(fǎng)問(wèn)類(lèi)對(duì)象數(shù)據(jù)成員
(1)普通指針
直接看例子:
#include <iostream>
#include <stdio.h>
#include <string>
#include <typeinfo>
#include <errno.h>
using namespace std;
class Rectangl {
public:
Rectangl(int length=1, int width=1):length(length), width(width) { }
int *get_length_ponter() { return &length; }
int length;
protected:
int width;
};
int main(void)
{
Rectangl rect1(2, 3);
int *p = NULL;
p = &rect1.length;
printf("p=%p\n", p);
printf("*p=%d\n", *p);
p = rect1.get_length_ponter();
printf("p=%p\n", p);
printf("*p=%d\n", *p);
return 0;
}
運(yùn)行結(jié)果:
p=0xbff36394
*p=2
p=0xbff36394
*p=2
例子分析:
例子中,直接使用普通指針存放對(duì)象數(shù)據(jù)成員地址,或者可以使用
getter取得數(shù)據(jù)成員地址,從運(yùn)行結(jié)果來(lái)看,一般指針存放對(duì)象數(shù)
據(jù)成員的地址是可行,這與使用一般指針存放結(jié)構(gòu)體地址是一樣的。
(2)類(lèi)成員指針
(1)與普通指針的區(qū)別
訪(fǎng)問(wèn)類(lèi)的數(shù)據(jù)成員,還可以使用類(lèi)成員指針,類(lèi)成員指針與普通指針區(qū)別在
于,普通指針存放的是絕對(duì)地址,但是類(lèi)成員指針存放的確是相對(duì)于類(lèi)對(duì)象
首字節(jié)地址的相對(duì)地址差,因此不能使用該地址直接訪(fǎng)問(wèn)數(shù)據(jù)成員,必須與
對(duì)象綁定后才能訪(fǎng)問(wèn)指向的成員。
(2)類(lèi)指針定義和使用格式
成員類(lèi)型 類(lèi)名::*pointer_name;
比如:
int Student::*p = &Student::age;
Student stu1("zhangsan", 20);
Student *stu2 = new Student("wangwu", 30);
int a = stu1.*age;
int a = stu2->*age;
(3)使用舉例
#include <iostream>
#include <stdio.h>
#include <string>
#include <typeinfo>
#include <errno.h>
using namespace std;
class Rectangl {
public:
Rectangl(int length=1, int width=1):length(length), width(width) { }
int length;
int width;
};
int main(void)
{
Rectangl rect1(2, 3);
Rectangl *rect2 = new Rectangl(2, 3);
int Rectangl::*p = NULL;
p = &Rectangl::width;
printf("%p\n", p);
printf("%d\n", rect1.*p);
printf("%d\n", rect2->*p);
return 0;
}
運(yùn)行結(jié)果:
0x4
3
3
例子分析:
從打印出來(lái)的地址來(lái)看,并不是一個(gè)絕對(duì)地址,只是一個(gè)地址相
對(duì)值,在通過(guò)類(lèi)成員指針訪(fǎng)問(wèn)類(lèi)數(shù)據(jù)成員時(shí),一定要注意與對(duì)象
綁定后才能訪(fǎng)問(wèn)成員,而且要注意訪(fǎng)問(wèn)格式。
(2)通過(guò)指針訪(fǎng)問(wèn)對(duì)象成員函數(shù)
(1)與使用指針訪(fǎng)問(wèn)數(shù)據(jù)成員對(duì)比
與前面的數(shù)據(jù)成員不同,對(duì)于成員函數(shù),如果希望通過(guò)函數(shù)指針訪(fǎng)問(wèn),只能通過(guò)類(lèi)
成員函數(shù)指針訪(fǎng)問(wèn),不能通過(guò)普通函數(shù)指針訪(fǎng)問(wèn),同樣,訪(fǎng)問(wèn)時(shí)也需要和對(duì)象綁定。
(2)格式
(1)定義格式
返回值 (類(lèi)名::pointer_name)(形參類(lèi)型列表)
樣子有點(diǎn)復(fù)復(fù)雜,可以使用typedef關(guān)鍵字進(jìn)行簡(jiǎn)化。
typedef 返回值 (類(lèi)名::pointer_name)(形參類(lèi)型列表)
這個(gè)時(shí)候pointer_name就是個(gè)別名,使用這個(gè)別名就可以用于
定義類(lèi)函數(shù)成員指針了。
如果有成員函數(shù)有const的話(huà),需要加cosnt。
(2)訪(fǎng)問(wèn)格式
(對(duì)象名.*函數(shù)指針)(實(shí)參)
或者:
(對(duì)象名->*函數(shù)指針)(實(shí)參)
注意使用時(shí)要加括號(hào),否者函數(shù)指針將優(yōu)先與參數(shù)列表先結(jié)合,顯然是不對(duì)的。
(3)使用例子
#include <iostream>
#include <stdio.h>
#include <string>
#include <typeinfo>
#include <errno.h>
using namespace std;
class Rectangl {
public:
Rectangl(int length=1, int width=1):length(length), width(width) { }
int get_length() const {
return length;
}
private:
int length;
int width;
};
typedef int (Rectangl::*fun_type)() const;
int main(void)
{
Rectangl rect1(2, 3);
Rectangl *rect2 = new Rectangl(2, 3);
fun_type get_funp;
get_funp = &Rectangl::get_length;
cout << get_funp <<endl;
cout << (rect1.*get_funp)() <<endl;
cout << (rect2->*get_funp)() <<endl;
return 0;
}
運(yùn)行結(jié)果:
1
2
2
例子分析:
與類(lèi)的數(shù)據(jù)成員指針的使用實(shí)際上是一樣的,使用時(shí)需要與對(duì)象綁定。
類(lèi)的函數(shù)成員指針變量存放的任然是一個(gè)地址偏移(相對(duì)地址差值)。
(4)類(lèi)成員作為傳參
(1)普通函數(shù)的傳參
#include <iostream>
#include <stdio.h>
#include <string>
#include <typeinfo>
#include <errno.h>
using namespace std;
class Rectangl {
public:
Rectangl(int length=1, int width=1):length(length), width(width) { }
int get_length() const {
return length;
}
int length;
int width;
};
typedef int (Rectangl::*fun_type)() const;
void show(fun_type funp, int Rectangl::*datap) {
Rectangl rect1(2, 3);
Rectangl *rect2 = &rect1;
cout << "長(zhǎng):" << (rect1.*funp)() << endl;
cout << "寬:" << rect2->*datap << endl;
}
int main(void)
{
fun_type get_funp;
get_funp = &Rectangl::get_length;
int Rectangl::*widthp = &Rectangl::width;
show(get_funp, widthp);
return 0;
}
(2)成員函數(shù)的傳參
#include <iostream>
#include <stdio.h>
#include <string>
#include <typeinfo>
#include <errno.h>
using namespace std;
class Rectangl;//類(lèi)聲明
typedef int (Rectangl::*fun_type)() const;
class Rectangl {
public:
Rectangl(int length=1, int width=1):length(length), width(width) { }
int get_length() const {
return length;
}
void show(fun_type funp, int Rectangl::*datap) {
cout << "長(zhǎng):" << (this->*funp)() << endl;
cout << "寬:" << this->*datap << endl;
}
int length;
int width;
};
int main(void)
{
Rectangl rect1(2, 3);
fun_type get_funp;
get_funp = &Rectangl::get_length;
int Rectangl::*widthp = &Rectangl::width;
rect1.show(get_funp, widthp);
return 0;
}