69pao国产精品视频-久久精品一区二区二三区-精品国产精品亚洲一本大道-99国产综合一区久久

深入解析C++中多態(tài)性和虛函數(shù)使用原理

深入解析c++中多態(tài)性和虛函數(shù)使用原理

 

1.為什么需要virtual

按照java的思維方式,在有了繼承和向上類型轉(zhuǎn)換(upcasting)之后,就可以實現(xiàn)多態(tài)性了。但是在c++中似乎并不能orz??紤]這種情況:

#include<iostream>
using std::cout;
using std::endl;
class a{
public:
void f() const{
  cout<<"class a's function"<<endl;
}
};
class b : public a{
public:
void f() const{
  cout<<"class b's function"<<endl;
}
};
int main(){
b b;
a *ptr_a = &b;
a &ref_a = b;
ptr_a->f();//print: class a's function
ref_a.f();//print: class a's function
}

在使用基類指針或引用調(diào)用一個派生類對象的函數(shù)時,我們發(fā)現(xiàn)程序仍然在調(diào)用基類的函數(shù),要想解決這種情況,就要引入virtual關(guān)鍵字,將上面代碼里的class a修改如下,main中的輸出就變成類b中f()的輸出了。

class a{
public:
virtual void f() const{
  cout<<"class a's function"<<endl;
}
};

那么為什么java不需要呢?因為virtual關(guān)鍵字實現(xiàn)功能的同時,會增加該類一些操作的時間和空間占用,c++將這部分占用的優(yōu)化決定權(quán)交給了程序員,以實現(xiàn)可能的效率提高;而java內(nèi)置了virtual的機(jī)制,沒有提高效率的選擇,但是簡化了編程。(關(guān)于virtual的具體機(jī)制,建議參考thinking in c++)

有兩點(diǎn)需要注意的:

  • 第一、當(dāng)使用基類指針指向派生類時,無法通過基類指針直接調(diào)用派生類中增加的函數(shù)(基類中沒有同名虛函數(shù)),除非將基類指針強(qiáng)制類型轉(zhuǎn)換為派生類指針。
  • 第二、只能通過基類指針或者引用來調(diào)用派生類對象,如果我們將一個派生類對象通過值傳遞的方式傳遞給基類對象,這個對象被真的切成一個基類對象,而不具有任何派生類的內(nèi)容。

 

2.純虛函數(shù)和抽象類

在類設(shè)計時,常常希望基類僅僅作為派生類的一個接口,被繼承實現(xiàn),而不會去創(chuàng)建基類對象,這時,可以在基類中定義純虛函數(shù),使其成為一個抽象類。定義純虛函數(shù)語法是在一個虛函數(shù)聲名的基礎(chǔ)上,加上=0。例如:virtual void f() = 0;注意:當(dāng)繼承一個抽象類時,必須實現(xiàn)其所有的純虛函數(shù),否則繼承出的類也是一個抽象類。

一般情況下,在基類中我們不會對純虛函數(shù)進(jìn)行實現(xiàn),但是c++提供了實現(xiàn)純虛函數(shù)的機(jī)制,這種方法可以讓我們定義一段公共代碼,使派生類可以公用。

class a{
public:
virtual void do() = 0; 
};
/*
*純虛函數(shù)不能作為inline函數(shù)實現(xiàn),要放在類外!
*/
void a::do(){
//一些公共代碼
}
class b : public a{
public:
void do() {
  a::do();
  //其他代碼
}
};

 

3.構(gòu)造函數(shù)與虛函數(shù)

如上文所說,定義一個虛函數(shù)時,需要做一些額外的工作,完成這些工作的代碼其實被秘密插入到類構(gòu)造函數(shù)的開頭部分。那么就有一個問題,如果我們在構(gòu)造函數(shù)中調(diào)用虛函數(shù)會發(fā)生什么現(xiàn)象?答案是,會調(diào)用這個虛函數(shù)的本地版本,即虛函數(shù)機(jī)制在構(gòu)造函數(shù)中不工作。
另外,構(gòu)造函數(shù)也不能被定義為虛函數(shù)。

 

4.虛析構(gòu)函數(shù)與純虛析構(gòu)函數(shù)

構(gòu)造函數(shù)不能被定義為虛函數(shù),而析構(gòu)函數(shù)可以,并且經(jīng)常被定義為虛函數(shù)。

#include<iostream>
using namespace std;
class base1{
public:
~base1(){cout<<"~base1()"<<endl;}
};
class base2{
public:
virtual ~base2(){cout<<"~base2()"<<endl;}
};
class derived1 : public base1{
public:
~derived1(){cout<<"~derived1()"<<endl;}
};
class derived2 : public base2{
public:
~derived2(){cout<<"~derived2()"<<endl;}
};
int main(){
base1* pd1 = new derived1();
base2* pd2 = new derived2();
delete pd1;
delete pd2;
}

上面代碼的控制臺輸出:

~base1()
~derived2()
~base2()

上面的代碼暴露出在使用多態(tài)性時,不把析構(gòu)函數(shù)定義成虛函數(shù)所帶來的影響。這種錯誤不會立刻使程序崩潰,但是它不知不覺中使內(nèi)存泄漏。

 

5.純虛析構(gòu)函數(shù)的應(yīng)用

在一些時候,我們需要定義一個抽象類,但是剛好沒有其他純虛函數(shù),這時候我們不妨將析構(gòu)函數(shù)定義為純虛的,因為作為基類的析構(gòu)函數(shù)本來就要求為虛函數(shù),將其進(jìn)一步定義為純虛函數(shù)并無太大不同。唯一需要注意的是,定義純虛析構(gòu)函數(shù)時必須為其提供函數(shù)體,如下。

class a{
public:
virtual ~a() = 0;
};
a::~a(){ }

class b:public a{
//不一定需要重定義析構(gòu)函數(shù),根據(jù)需要
}

還要注意一點(diǎn),在析構(gòu)函數(shù)中,虛機(jī)制也是不存在的,可通過下面的代碼體會。

#include<iostream>
using namespace std;
class base{
public:
virtual ~base(){cout<<"~base()"<<endl;f();}
virtual void f(){cout<<"base::f()"<<endl;}
};
class derived : public base{
public:
~derived(){cout<<"~derived()"<<endl;}
void f(){cout<<"derived::f()"<<endl;}
};
int main(){
base * pb = new derived();
delete pb;
}

控制臺輸出為:

~derived()
~base()
base::f()

以上就是深入解析c++中多態(tài)性和虛函數(shù)使用原理的詳細(xì)內(nèi)容,更多關(guān)于c++多態(tài)性虛函數(shù)的資料請關(guān)注碩編程其它相關(guān)文章!

下一節(jié):linux/c++多線程實例學(xué)習(xí)十字路口車輛調(diào)度

c語言編程技術(shù)

相關(guān)文章