C++中virtual关键字的用法
關(guān)于virtual關(guān)鍵字的用法總結(jié)如下,有錯(cuò)誤或者總結(jié)不到位的情況請(qǐng)能幫本人指出,非常感謝!
Virtual是C++ OO機(jī)制中很重要的一個(gè)關(guān)鍵字。只要是學(xué)過C++的人都知道在類Base中加了Virtual關(guān)鍵字的函數(shù)就是虛擬函數(shù)。
基類的函數(shù)調(diào)用如果有virtual則根據(jù)多態(tài)性調(diào)用派生類的,如果沒有virtual則是正常的靜態(tài)函數(shù)調(diào)用,還是調(diào)用基類的。
1、虛函數(shù)的應(yīng)用
看下面的一段代碼的輸出結(jié)果:
class Base
{
public:Base(){}
public:
?????? virtual void print(){cout<<"Base";}
};
?
class Derived:public Base
{
public:Derived(){}
public:
?????? void print(){cout<<"Derived";}
};
?
int main()
{
?????? Base *point=new Derived();
?????? point->print();
}
Output:
Derived?
這也許會(huì)使人聯(lián)想到函數(shù)的重載,但稍加對(duì)比就會(huì)發(fā)現(xiàn)兩者是完全不同的:
(1)重載的幾個(gè)函數(shù)必須在同一個(gè)類中;
覆蓋的函數(shù)必須在有繼承關(guān)系的不同的類中
(2)覆蓋的幾個(gè)函數(shù)必須函數(shù)名、參數(shù)、返回值都相同;
重載的函數(shù)必須函數(shù)名相同,參數(shù)不同。參數(shù)不同的目的就是為了在函數(shù)調(diào)用的時(shí)候編譯器能夠通過參數(shù)來判斷程序是在調(diào)用的哪個(gè)函數(shù)。這也就很自然地解釋了為什么函數(shù)不能通過返回值不同來重載,因?yàn)槌绦蛟谡{(diào)用函數(shù)時(shí)很有可能不關(guān)心返回值,編譯器就無法從代碼中看出程序在調(diào)用的是哪個(gè)函數(shù)了。
(3)覆蓋的函數(shù)前必須加關(guān)鍵字Virtual;
重載和Virtual沒有任何瓜葛,加不加都不影響重載的運(yùn)作。
再看下面林瑞博士講解的一段關(guān)于關(guān)鍵字Virtual的用法
#include <iostream.h>
class Base
{
public:
virtual void f(float x){ cout << "Base::f(float) " << x << endl; }
void g(float x){ cout << "Base::g(float) " << x << endl; }
void h(float x){ cout << "Base::h(float) " << x << endl; }
};
?
class Derived : public Base
{
public:
virtual void f(float x){ cout << "Derived::f(float) " << x << endl; }
void g(int x){ cout << "Derived::g(int) " << x << endl; }
void h(float x){ cout << "Derived::h(float) " << x << endl; }
};
?
void main(void)
{
Derived d;
Base *pb = &d;
Derived *pd = &d;
// Good : behavior depends solely on type of the object
pb->f(3.14f); // Derived::f(float) 3.14
pd->f(3.14f); // Derived::f(float) 3.14
// Bad : behavior depends on type of the pointer
pb->g(3.14f); // Base::g(float) 3.14
pd->g(3.14f); // Derived::g(int) 3 (surprise!)
// Bad : behavior depends on type of the pointer
pb->h(3.14f); // Base::h(float) 3.14 (surprise!)
pd->h(3.14f); // Derived::h(float) 3.14
}
?
bp 和dp 指向同一地址,按理說運(yùn)行結(jié)果應(yīng)該是相同的,而事實(shí)上運(yùn)行結(jié)果不同,所以他把原因歸結(jié)為C++的隱藏規(guī)則,其實(shí)這一觀點(diǎn)是錯(cuò)的。決定bp和dp調(diào)用函數(shù)運(yùn)行結(jié)果的不是他們指向的地址,而是他們的指針類型。“只有在通過基類指針或引用間接指向派生類子類型時(shí)多態(tài)性才會(huì)起作用”(C++ Primer 3rd Edition)。pb是基類指針,pd是派生類指針,pd的所有函數(shù)調(diào)用都只是調(diào)用自己的函數(shù),和多態(tài)性無關(guān),所以pd的所有函數(shù)調(diào)用的結(jié)果都輸出Derived::是完全正常的;pb的函數(shù)調(diào)用如果有virtual則根據(jù)多態(tài)性調(diào)用派生類的,如果沒有virtual則是正常的靜態(tài)函數(shù)調(diào)用,還是調(diào)用基類的,所以有virtual的f函數(shù)調(diào)用輸出Derived::,其它兩個(gè)沒有virtual則還是輸出Base::很正常啊,nothing surprise!?
所以并沒有所謂的隱藏規(guī)則,雖然《高質(zhì)量C++/C 編程指南》是本很不錯(cuò)的書,可大家不要迷信哦。記住“只有在通過基類指針或引用間接指向派生類子類型時(shí)多態(tài)性才會(huì)起作用”。
2、純虛函數(shù)
純虛函數(shù)定義如下:
C++語言為我們提供了一種語法結(jié)構(gòu),通過它可以指明,一個(gè)虛擬函數(shù)只是提供了一個(gè)可被子類型改寫的接口。但是,它本身并不能通過虛擬機(jī)制被調(diào)用。這就是純虛擬函數(shù)(purevirtual function)。 純虛擬函數(shù)的聲明如下所示:
class Query {
public:
// 聲明純虛擬函數(shù)
virtual ostream& print( ostream&=cout ) const = 0;
// ...
};
這里函數(shù)聲明后面緊跟賦值0。
包含一個(gè)或多個(gè)純虛擬函數(shù)的類被編譯器識(shí)別為抽象基類。抽象基類不能被實(shí)例化,一般用于繼承。抽象基類只能作為子對(duì)象出現(xiàn)在后續(xù)的派生類中
3、 虛擬繼承(virtual public)
在多繼承下,虛繼承就是為了解決菱形繼承中,B,C都繼承了A,D繼承了B,C,那么D關(guān)于 A的引用只有一次,而不是 普通繼承的 對(duì)于A引用了兩次……
格式:可以采用public、protected、private三種不同的繼承關(guān)鍵字進(jìn)行修飾,只要確保包含virtual就可以了。
class A {void f1(){}; }; class B : public virtual A{void f2(){}; }; 虛繼承:在繼承定義中包含了virtual關(guān)鍵字的繼承關(guān)系;虛基類:在虛繼承體系中的通過virtual繼承而來的基類,
#include?
using namespace std;
class Person{
? ?public: ? ?Person(){ cout<<"Person構(gòu)造"<<ENDL; }
? ? ? ? ? ?~Person(){ cout<<"Person析構(gòu)"<<ENDL; }
};
class Teacher : virtual public Person{
? ?public: ? ?Teacher(){ cout<<"Teacher構(gòu)造"<<ENDL; }
? ? ? ? ? ? ~Teacher(){ out<<"Teacher析構(gòu)"<<ENDL; }
};
class Student : virtual public Person{
? public: ? ? ?Student(){ cout<<"Student構(gòu)造"<<ENDL; }
? ? ? ? ? ? ?~Student(){ cout<<"Student析構(gòu)"<<ENDL; }
};
class TS : public Teacher, ?public Student{
public: ? ? ? ? ? ?TS(){ cout<<"TS構(gòu)造"<<ENDL; }
? ? ? ? ? ? ? ? ?~TS(){ cout<<"TS析構(gòu)"<<ENDL; }
};
int main(int argc,char* argv[])
{
TS ts;
return 0;
}
這段代碼的終端輸出結(jié)果為:
Person構(gòu)造
Teacher構(gòu)造
Student構(gòu)造
TS構(gòu)造
TS析構(gòu)
Student析構(gòu)
Teacher析構(gòu)
Person析構(gòu)
當(dāng)Teacher類和Student類沒有虛繼承Person類的時(shí)候,也就是把virtual去掉時(shí)候終端輸出的結(jié)果為:
Person構(gòu)造
Teacher構(gòu)造
Person構(gòu)造
Student構(gòu)造
TS構(gòu)造
TS析構(gòu)
Student析構(gòu)
Person析構(gòu)
Teacher析構(gòu)
Person析構(gòu)
? ? ?大家可以很清楚的看到這個(gè)結(jié)果明顯不是我們所期望的。我們?cè)跇?gòu)造TS的時(shí)候需要先構(gòu)造他的基類,也就是Teacher類和Student類。而Teacher類和Student類由都繼承于Person類。這樣就導(dǎo)致了構(gòu)造TS的時(shí)候?qū)嵗藘蓚€(gè)Person類。同樣的道理,析構(gòu)的時(shí)候也是析構(gòu)了兩次Person類,這是非常危險(xiǎn)的,也就引發(fā)出了virtual的第三種用法,虛析構(gòu)。
關(guān)于虛繼承的相關(guān)功能,本人也是一知半解,后續(xù)再做深入的研究
總結(jié)
以上是生活随笔為你收集整理的C++中virtual关键字的用法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 100个Python实战项目(一)使用
- 下一篇: c/c++整理--c++面向对象(3)