日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++ virtual 析构函数

發布時間:2023/12/20 c/c++ 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++ virtual 析构函数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

copy自:http://zxjgoodboy.blog.sohu.com/61482463.html

在此基礎上稍作修改

?

C++中虛析構函數的作用

我們知道,用C++開發的時候,用來做基類的類的析構函數一般都是虛函數。可是,為什么要這樣做呢?下面用一個小例子來說明:?

有下面的兩個類:

[cpp] view plaincopy
  • #include?<iostream>??
  • using?namespace?std;??
  • class?ClxBase??
  • {??
  • public:??
  • ????ClxBase()?{};??
  • ????virtual?~ClxBase()?{cout<<"AAA"<<endl;};??
  • ????virtual?void?DoSomething()?{?cout?<<?"Do?something?in?class?ClxBase!"?<<?endl;?};??
  • };??
  • class?ClxDerived?:?public?ClxBase??
  • {??
  • public:??
  • ????ClxDerived()?{};??
  • ????~ClxDerived()?{?cout?<<?"Output?from?the?destructor?of?class?ClxDerived!"?<<?endl;?};??
  • ????void?DoSomething()?{?cout?<<?"Do?something?in?class?ClxDerived!"?<<?endl;?};??
  • };??
  • int?main()??
  • {??
  • ????ClxBase?*pTest?=?new?ClxDerived;??
  • ????pTest->DoSomething();??
  • ????delete?pTest;??
  • } ?
  • 輸出結果:

    Do something in class ClxDerived!

    Output from the destructor of class ClxDerived!

    AAA

    ?這個很簡單,非常好理解。

    但是,如果把類ClxBase析構函數前的virtual去掉,那輸出結果就是下面的樣子了:

    Do something in class ClxDerived!

    AAA

    也就是說,類ClxDerived的析構函數根本沒有被調用!一般情況下類的析構函數里面都是釋放內存資源,而析構函數不被調用的話就會造成內存泄漏。我想所有的C++程序員都知道這樣的危險性。當然,如果在析構函數中做了其他工作的話,那你的所有努力也都是白費力氣。
    ????所以,文章開頭的那個問題的答案就是--這樣做是為了當用一個基類的指針刪除一個派生類的對象時,派生類的析構函數會被調用。
    ????當然,并不是要把所有類的析構函數都寫成虛函數。因為當類里面有虛函數的時候,編譯器會給類添加一個虛函數表,里面來存放虛函數指針,這樣就會增加類的存儲空間。所以,只有當一個類被用來作為基類的時候,才把析構函數寫成虛函數。

    ?

    你需要virtual析構函數嗎

    使用VC的class wizard自動生成一個類,會得到兩個空的函數:構造函數和virtual析構函數。為什么析構函數要聲明成virtual呢?

    如果一個類要被使用成多態(polymorphic)的,那么這個virtual是必須的。比如:

    [cpp]?view plaincopy
  • #include?<iostream>??
  • class?Animal??
  • {??
  • ??char*?ap;??
  • public:??
  • ???
  • ??Animal()??
  • ??{??
  • ????ap?=?new?char;??
  • ????std::cout?<<?"Animal?ctor"?<<?std::endl;??
  • ??}??
  • ??virtual?void?foo()??
  • ??{??
  • ????std::cout?<<?"Animal::foo"?<<?std::endl;??
  • ??}??
  • ??virtual?~Animal()??
  • ??{??
  • ????std::cout?<<?"Animal?dtor"?<<?std::endl;??
  • ????delete?ap;??
  • ??}??
  • };??
  • class?Dog?:?public?Animal??
  • {??
  • ??char*?dp;??
  • public:??
  • ??Dog()??
  • ??{??
  • ????dp?=?new?char;??
  • ????std::cout?<<?"Dog?ctor"?<<?std::endl;??
  • ??}??
  • ??virtual?void?foo()??
  • ??{??
  • ????std::cout?<<?"Dog::foo"?<<?std::endl;??
  • ??}??
  • ??virtual?~Dog()??
  • ??{??
  • ????delete?dp;??
  • ????std::cout?<<?"Dog?dtor"?<<?std::endl;??
  • ??}??
  • };??
  • int?main()??
  • {??
  • ??Animal*?pa?=?new?Dog();??
  • ??pa->foo();??
  • ??delete?pa;??
  • ??return?0;??
  • }??
  • ?

    ?

    delete pa 實際上相當于:
    ?pa->~Animal();
    ?釋放pa所指向的內存(或許是free(pa))。
    在 這里,因為~Animal()是virtual的,盡管是通過Animal類型的指針調用的,根據v-table的信息,~Dog()被正確調用到。如果 把virtual屬性去掉,那么被調用的是~Animal(),Dog類的構造函數被調用而析構函數未被調用,構造函數中分配的資源沒有釋放,從而產生了 內存泄漏。析構函數缺省聲明為virtual,就可以避免這一問題。

    可另一個問題是,有時virtual是不需要的。如果一個類不會被繼承,比如一個utility類,該類完全是靜態方法;或者一些類盡管可能會被繼承,但不會被使用成多態的,即除了析構函數外,沒有其他的方法是virtual的,這時就可以把virtual屬性去掉。

    去掉析構函數的virtual屬性后,因為該類中沒有其他的virtual函數,所以編譯時不會生成v-table,這樣就節省了編譯時間,并減少了最終生成的程序的大小。更重要的是,遵從這一規則,給該類的維護者一個信息,即該類不應被當作多態類使用。

    同樣,當作一個抽象時,如果你模仿Java的interface,聲明了如下的虛基類:

    [cpp]?view plaincopy
  • class?AbstractBase??
  • {??
  • ?virtual?method1()?=?0;??
  • ?virtual?method2()?=?0;??
  • };??
  • ?

    ?

    那么應該給它增加一個空的virtual析構函數:
    ?virtual ~AbstractBase(){}

    如果你對COM比較熟悉,可能會注意到,COM interface中并沒有這個virutal構造函數。這是因為,COM通過使用引用計數的機制來維護對象。當你使用完一個COM對象,調用Release()時,COM的內部實現檢查引用技術是否為零。如果是,則調用
    ?delete this;
    因為Release()是virtual的,所以該COM對象對應的正確的派生類被調用,delete this會調用正確的析構函數,達到了使用virtual析構函數的效果。

    ?

    ?

    定義純虛析構函數(pure virtual destructor)zz

    純虛成員函數通常沒有定義;它們是在抽象類中聲明,然后在派生類中實現。比如說下面的例子:

    [cpp]?view plaincopy
  • class?File?//an?abstract?class??
  • {??
  • public:??
  • ?virtual?int?open(const?string?&?path,?int?mode=0x666)=0;??
  • ?virtual?int?close()=0;??
  • //...??
  • };???
  • ?

    但是,在某些情況下,我們卻需要定義一個純虛成員函數,而不僅僅是聲明它。最常見的例子是純虛析構函數。在聲明純虛析構函數時,不要忘了同時還要定義它。

    [cpp]?view plaincopy
  • class?File?//abstract?class??
  • {??
  • public:??
  • ?virtual?~File()=0;?//declaration?of?a?pure?virtual?dtor??
  • };??
  • File::~File()?{}?//definition?of?dtor???
  • ?

    ?

    為什么說定義純虛析構函數是非常重要的

    ?

    派生類的析構函數會自動調用其基類的析構函數。這個過程是遞歸的,最終,抽象類的純虛析構函數也會被調用。

    如果純虛析構函數只被聲明而沒有定義,那么就會造成運行時(runtime)崩潰。(在很多情況下,這個錯誤會出現在編譯期,但誰也不擔保一定會是這樣。)純虛析構函數的啞元實現(dummy implementation,即空實現)能夠保證這樣的代碼的安全性。

    [cpp] view plaincopy
  • class?DiskFile?:?public?File??
  • {??
  • public:??
  • ?int?open(const?string?&?pathname,?int?mode);??
  • ?int?close();??
  • ?~DiskFile();??
  • };??
  • File?*?pf?=?new?DiskFile;??
  • //.?.?.??
  • delete?pf;?//OK,?ultimately?invokes?File::~File()???
  • 在某些情況下定義其它純虛成員函數可能也是非常有用的(比如說在調試應用程序以及記錄應用程序的日志時)。例如,在一個不應該被調用,但是由于一個缺陷而被調用的基類中,如果有一個純虛成員函數,那么我們可以為它提供一個定義。

    [cpp] view plaincopy
  • class?Abstract??
  • {??
  • public:??
  • ?virtual?int?func()=0;??
  • //..??
  • };??
  • int?Abstract::func()??
  • {??
  • std::cerr<<"got?called?from?thread?"?<<?thread_id<<??
  • ?????????????"at:?"<<gettimeofday()<<std::endl;??
  • }???
  • 這樣,我們就可以記錄所有對純虛函數的調用,并且還可以定位錯誤代碼;不為純虛函數提供定義將會導致整個程序無條件地終止。


    虛構造函數(virtual constructor)

    C++不支持直接的虛構造函數。虛 擬機制的設計目的是使程序員在不完全了解細節(比如只知該類實現了某個界面,而不知該類確切是什么東東)的情況下也能使用對象。但是,要建立一個對象,可 不能只知道“這大體上是什么”就完事——你必須完全了解全部細節,清楚地知道你要建立的對象是究竟什么。所以,構造函數當然不能是虛的了。但是,可通過虛函數?virtual clone()(對于拷貝構造函數)或虛函數?virtual create()(對于默認構造函數),得到虛構造函數產生的效果。

    注意:子類成員函數clone()的返回值類型故意與父類成員函數clone()的不同。這種特征被稱為“協變的返回類型”(Covariant Return Types),該特征最初并不是C++語言的一部分,VC6.0以下版本編譯器不支持這樣的寫法。

    虛析構函數(virtual destructor)

    當你可能通過基類指針刪除派生類對象時,建議使用虛析構函數。虛函數綁定到對象的類的代碼,而不是指針/引用的類。如果基類有虛析構函數,delete basePtr(基類指針)時,*basePtr?的對象類型的析構函數被調用,而不是該指針的類型的析構函數。

    簡單講,這個類有虛函數就應該有虛析構函數。一旦你在類中加上了一個虛函數,你就已經需要為每一個對象支付空間代價(每個對象一個指針),所以這時使析構函數成為虛擬的通常不會額外付出什么。

    對于那些trivial且沒有子類的類,虛析構函數只會增加開銷,不要使用

    總結

    以上是生活随笔為你收集整理的C++ virtual 析构函数的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。