接上篇繼續(xù)學(xué)習(xí)多態(tài)、類型轉(zhuǎn)換
多態(tài):
多態(tài)(Polymorphism)按字面的意思就是“多種狀態(tài)”。在面向?qū)ο笳Z言中,接口的多種不同的實現(xiàn)方式即為多態(tài)
我們這里使用的模型是:人,英國人,中國人,都有吃飯的方法,人用手吃飯,英國人用刀叉,而中國人用筷子。我們問“這個人怎么吃飯的?”,應(yīng)該根據(jù)其國別來回答,而不是簡單地說“用手吃”。這就是多態(tài)。
在c++中多態(tài)是使用虛函數(shù)來實現(xiàn)的:
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Human {
private:
int a;
public:
//定義虛函數(shù)
virtual void eating(void) { cout<<"use hand to eat"<<endl; }
};
class Englishman : public Human {
public:
//重寫虛函數(shù)
void eating(void) { cout<<"use knife to eat"<<endl; }
};
class Chinese : public Human {
public:
void eating(void) { cout<<"use chopsticks to eat"<<endl; }
};
//實現(xiàn)虛函數(shù)
void test_eating(Human& h){
h.eating();
}
int main(int argc, char **argv){
Human h;
Englishman e;
Chinese c;
test_eating(h);
test_eating(e);
test_eating(c);
cout<<"sizeof(Human) = "<<sizeof(h)<<endl;
cout<<"sizeof(Englishman) = "<<sizeof(e)<<endl;
cout<<"sizeof(Chinese) = "<<sizeof(c)<<endl;
return 0;
}
輸出:
use hand to eat
use knife to eat
use chopsticks to eat
原理:
對于虛函數(shù),采用動態(tài)聯(lián)編:有虛函數(shù)的對象里有一個指針,指向虛函數(shù)表;
調(diào)用虛函數(shù)時,會根據(jù)對象里的指針找到表,從表中取出函數(shù)來執(zhí)行
對于非虛函數(shù),采用靜態(tài)聯(lián)編:編譯時就確定調(diào)用哪個函數(shù)
差別:靜態(tài)聯(lián)編效率高,動態(tài)聯(lián)編支持多態(tài)
除此之外,多態(tài)的使用也是有條件的:
- 1.使用指針或引用來使用對象時,才有多態(tài),傳值時,無多態(tài)
test_func(Human* h):
test_func(Human& h):使用指針或引用來使用對象時,才有多態(tài)
test_func(Human h):傳值時,無多態(tài)
- 2.靜態(tài)成員函數(shù)不能是虛函數(shù)
- 3.內(nèi)聯(lián)函數(shù)、構(gòu)造函數(shù)不能是虛函數(shù)
- 4.析構(gòu)函數(shù)一般都聲明為虛函數(shù)
- 5.重載:函數(shù)參數(shù)不同,不可設(shè)為虛函數(shù)
覆蓋:函數(shù)參數(shù)、返回值相同,可以設(shè)為虛函數(shù) - 6.返回值例外:
函數(shù)參數(shù)相同,但是返回值是當(dāng)前對象的指針或引用時,也可以設(shè)為虛函數(shù)
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Human {
private:
int a;
public:
virtual void eating(void) { cout<<"use hand to eat"<<endl; }
//析構(gòu)函數(shù)是虛函數(shù)
virtual ~Human() { cout<<"~Human()"<<endl; }
//返回值是引用或指針時可以使用虛函數(shù)
virtual Human* test(void) {cout<<"Human's test"<<endl; return this; }
};
class Englishman : public Human {
public:
void eating(void) { cout<<"use knife to eat"<<endl; }
virtual ~Englishman() { cout<<"~Englishman()"<<endl; }
virtual Englishman* test(void) {cout<<"Englishman's test"<<endl; return this; }
};
class Chinese : public Human {
public:
void eating(void) { cout<<"use chopsticks to eat"<<endl; }
virtual ~Chinese() { cout<<"~Chinese()"<<endl; }
virtual Chinese* test(void) {cout<<"Chinese's test"<<endl; return this; }
};
void test_eating(Human& h){
h.eating();
}
void test_return(Human& h){
h.test();
}
int main(int argc, char **argv){
Human h;
Englishman e;
Chinese c;
test_return(h);
test_return(e);
test_return(c);
return 0;
}
類型轉(zhuǎn)換
c中隱式類型轉(zhuǎn)換是由編譯器執(zhí)行的:
#include <stdio.h>
int main(int argc, char **argv){
double d = 100.1;
int i = d; // double??int
char *str = "100ask.taobao.com";
int *p = str; // char *??int *
printf("i = %d, str = 0x%x, p = 0x%x\n", i, str, p);
return 0;
}
c中顯示類型轉(zhuǎn)換用()實現(xiàn):
#include <stdio.h>
int main(int argc, char **argv){
double d = 100.1;
int i = d;
char *str = "100ask.taobao.com";
int *p = (int *)str;
printf("i = %d, str = 0x%x, p = 0x%x\n", i, (unsigned int)str, (unsigned int)p);
return 0;
}
c++中使用reinterpret_cast()來實現(xiàn)轉(zhuǎn)換:
#include <stdio.h>
int main(int argc, char **argv){
double d = 100.1;
int i = d;
char *str = "100ask.taobao.com";
int *p = reinterpret_cast<int *>(str);
printf("i = %d, str = 0x%x, p = 0x%x\n", i, reinterpret_cast<unsigned int>(str), reinterpret_cast<unsigned int>(p));
return 0;
}
reinterpret_cast相當(dāng)于C中的小括號的類型轉(zhuǎn)換。但是它不能轉(zhuǎn)換帶const的。
可以使用const_cast()去掉const屬性再轉(zhuǎn)換:
#include <stdio.h>
int main(int argc, char **argv){
double d = 100.1;
int i = d;
const char *str = "100ask.taobao.com";
char *str2 = const_cast<char *>(str);
int *p = reinterpret_cast<int *>(str2);
printf("i = %d, str = 0x%x, p = 0x%x\n", i, reinterpret_cast<unsigned int>(str), reinterpret_cast<unsigned int>(p));
return 0;
}
除此之外,C++還提供動態(tài)轉(zhuǎn)換dynamic_cast(),但是只能用于含有虛函數(shù)類里面,因為它需要查找虛函數(shù)表來確定類的信息:
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Human {
private:
int a;
public:
virtual void eating(void) { cout<<"use hand to eat"<<endl; }
virtual ~Human() { cout<<"~Human()"<<endl; }
virtual Human* test(void) {cout<<"Human's test"<<endl; return this; }
};
class Englishman : public Human {
public:
void eating(void) { cout<<"use knife to eat"<<endl; }
virtual ~Englishman() { cout<<"~Englishman()"<<endl; }
virtual Englishman* test(void) {cout<<"Englishman's test"<<endl; return this; }
};
class Chinese : public Human {
public:
void eating(void) { cout<<"use chopsticks to eat"<<endl; }
virtual ~Chinese() { cout<<"~Chinese()"<<endl; }
virtual Chinese* test(void) {cout<<"Chinese's test"<<endl; return this; }
};
void test_eating(Human& h){
Englishman *pe;
Chinese *pc;
h.eating();
/* 想分辨這個"人"是英國人還是中國人? */
if (pe = dynamic_cast<Englishman *>(&h))
cout<<"This human is Englishman"<<endl;
if (pc = dynamic_cast<Chinese *>(&h))
cout<<"This human is Chinese"<<endl;
}
int main(int argc, char **argv){
Human h;
Englishman e;
Chinese c;
test_eating(h);
test_eating(e);
test_eating(c);
return 0;
}
輸出:
use hand to eat
use knife to eat
use chopsticks to eat
use hand to eat
use knife to eat
This human is Englishman
use chopsticks to eat
This human is Chinese
~Chinese()
~Human()
~Englishman()
~Human()
~Human()
這樣我們就能再運行時確定多態(tài)的具體類型。
動態(tài)轉(zhuǎn)換的格式:dynamic_cast < type-id > ( expression )
把expression轉(zhuǎn)換成type-id類型的對象。
Type-id必須是類的指針、類的引用或者void *;
如果type-id是類指針類型,那么expression也必須是一個指針;
如果type-id是一個引用,那么expression也必須是一個引用。
動態(tài)類型是運行是轉(zhuǎn)換,如果想在編譯時轉(zhuǎn)換可以使用格式:static_cast():
int main(int argc, char **argv){
Human h;
Guangximan g;
Englishman *pe;
//下行轉(zhuǎn)換是可以的但不安全
pe = static_cast<Englishman *>(&h);
//上行轉(zhuǎn)換,但是不能轉(zhuǎn)換成無關(guān)的Englishman類型,在編譯時就報錯,只能轉(zhuǎn)換成相關(guān)的Chinese人
Chinese *pc = static_cast<Chinese *>(&g);
return 0;
}
總結(jié)下靜態(tài)轉(zhuǎn)換:
- 用于類層次結(jié)構(gòu)中基類和子類之間指針或引用的轉(zhuǎn)換。
- 進行上行轉(zhuǎn)換(把子類的指針或引用轉(zhuǎn)換成基類表示)是安全的;
- 進行下行轉(zhuǎn)換(把基類指針或引用轉(zhuǎn)換成子類指針或引用)時,由于沒有動態(tài)類型檢查,所以是不安全的。