虛函數和純虛函數和析構函數


 

 

記錄了學習虛函數與純虛函數中有一些疑問,以及平常可能不注意的地方。

Q0:虛函數是怎么實現的?

0:簡單的說,是通過虛函數表實現的。如果一個類中含有虛函數,則系統會為這個類分配一個指針成員指向一張虛函數表(vtbl),表中每一項指向一個虛函數的地址,實現上就是一個函數指針的數組。

 

Q1:基類函數加上virtual關鍵字,派生類不加,那么派生類的同名函數是虛函數嗎?

1.C++繼承中,如果基類定義了一個虛函數,那么派生類中的同名函數不用加virtual(也可以加)關鍵字,也可以使該函數為虛函數。因為派生類繼承了基類的屬性,所以派生類中同名函數被視為與基類有相同屬性的函數。

如果基類為純虛函數,那么派生類中也不用聲明virtual,但是必須實現純虛函數。

 

純虛函數基類中定義:virtual fun() = 0;派生類可以有不同的實現,即可以稱在基類中提供了該函數的接口。

Q2:那為什么需要這個接口呢,比如我直接可以在子類中分別寫同名函數,然后一一實現。

2.看到的一個很形象例子:比如基類的接口類似與電腦的USB接口,不同的設備都可以插入訪問數據,U盤、鼠標、鍵盤;如果分別實現的話,那么就需要在電腦上加三個不同類型的接口,而純虛函數的作用,則只需要這一個接口來對不同的需求進行操作,非常方便。

 

Q3:構造函數可以是虛函數嗎?析構函數呢?

3:構造函數不能是虛函數,因為要構造一個對象,就必須知道構造了什么;析構函數一般定義為虛函數,稱為虛析構函數(指向派生類對象的基類指針被delete時,會調用派生類析構函數、基類的析構函數,否則只會調用基類的析構函數),可以定義為純虛函數。

(可以參考 http://www.cnblogs.com/chio/archive/2007/09/10/888260.html)
講了平常不會注意的地方:
純虛函數也可以定義函數體;
6) 析構函數可以是純虛的,但純虛析構函數必須有定義體,因為析構函數的調用是在子類中隱含的。 
7) 非純的虛函數必須有定義體,不然是一個錯誤。

 

虛函數的作用:(例子) 

 1 class Base
 2 {
 3 public:
 4     virtual void Draw(){};
 5 };
 6 
 7 class DerivedA:public Base
 8 {
 9 public:
10     DerivedA(){};
11     void Draw()
12     {
13         printf("A draw\n");
14     }
15 };
16 
17 class DerivedB:public Base
18 {
19 public:
20     DerivedB(){};
21     void Draw()
22     {
23         printf("B draw\n");
24     }
25 };    

如果要DerivedA或者DerivedB調用Draw(),可以分別new它們的對象,然后調用Draw(),但是如果還有其他的派生類,則需要定義並new很多個對象;

那么,虛函數的作用:

Base* base = new DerivedA; 或者 Base* base = new DerivedB;

base->Draw(); //按照new的對象,對應調用派生類的Draw()函數,只需要定義一個base指針即可。

 

純虛函數的作用:(例子)

class Vehicle
{
public:
    virtual void Print()=0//純虛函數的定義
};

class Car:public Vehicle
{
public:
    virtual void Print(){cout<<"Car"<<endl;};
};

class Bike:public Vehicle
{
public:
    virtual void Print(){cout<<"Bike"<<endl;};
};

void main()
{
    Car c;
    Bike b;
    b.Print();  //輸出Bike
    c.Print();  //輸出Car
}

這里Vehicle為抽象類,也叫純虛類;定義了print()=0;派生類Car和Bike實現了print()函數,按照對象調用各自的print()函數。

 

Q4:如何通過指向派生類對象的指針或引用調用基類對象的虛函數?(參考https://blog.csdn.net/chijianxingfeng/article/details/8870387)

例一:

#include <iostream>
using namespace std;
 
class A
{
public:
    virtual void show()
    {
        cout<<"in A::show()\n";
    }
};
 
class B:public A
{
public:
    void show()
    {
        cout<<"in B::show()\n";
    }
};
 
int main()
{
    A a;
    //通過派生類對象的引用pb 實現了調用基類中虛函數show(),,
    //如果把 A中show() 前面的virtual去掉, 則調用的就是B 中的show()
    B &pb = static_cast<B&>(a);
    pb.show();    //調用的是基類 A的 show();
    return 0;
}
View Code

輸出:

例二:

#include <iostream>
using namespace std;
 
class A
{
public:
    virtual void show()
    {
        cout<<"in A::show()\n";
    }
    void callfunc()
    {
        show();
    }
};
 
class B:public A
{
public:
    void show()
    {
        cout<<"in B::show()\n";
    }
};
 
int main()
{
    B b;
    b.callfunc();    //調用的是A::callfunc(),,但在A::callfunc()調用的是B::show()
            //這就是一個虛調用
    A a;
    a.callfunc();    //這里調用的是A::show()
    return 0;
}
View Code

輸出:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM