溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C++中多態性和虛函數的使用原理

發布時間:2023-09-05 11:42:02 來源:億速云 閱讀:101 作者:栢白 欄目:開發技術

本篇文章和大家了解一下C++中多態性和虛函數的使用原理。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有所幫助。

1.為什么需要virtual

按照Java的思維方式,在有了繼承和向上類型轉換(upcasting)之后,就可以實現多態性了。但是在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
}

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

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

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

有兩點需要注意的:

  • 第一、當使用基類指針指向派生類時,無法通過基類指針直接調用派生類中增加的函數(基類中沒有同名虛函數),除非將基類指針強制類型轉換為派生類指針。

  • 第二、只能通過基類指針或者引用來調用派生類對象,如果我們將一個派生類對象通過值傳遞的方式傳遞給基類對象,這個對象被真的切成一個基類對象,而不具有任何派生類的內容。

2.純虛函數和抽象類

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

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

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

3.構造函數與虛函數

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

4.虛析構函數與純虛析構函數

構造函數不能被定義為虛函數,而析構函數可以,并且經常被定義為虛函數。

#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()

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

5.純虛析構函數的應用

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

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

class B:public A{
//不一定需要重定義析構函數,根據需要
}

還要注意一點,在析構函數中,虛機制也是不存在的,可通過下面的代碼體會。

#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++中多態性和虛函數的使用原理的簡略介紹,當然詳細使用上面的不同還得要大家自己使用過才領會。如果想了解更多,歡迎關注億速云行業資訊頻道哦!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

c++
AI

邻居新婚少妇真紧_国产精品28p_99久久e免费热视烦_99尹人香蕉国产免费天天