精華區beta C_and_CPP 關於我們 聯絡資訊
class A { public: ........一些pure virtual function virtual ~A() = 0; }; 有其他class會繼承A 其實我code中 class是寫interface 其實就是struct 但他的destructor不給我宣告成pure 他一定要我定義 所以一定要改成 virtual ~A(){}... 不然就是 外頭在 定義一次A::~A(){} 但我覺得很沒道理 interface不就是包一堆pure virtual function 給別人繼承後 override嗎? 還是其實在外面寫個空的destructor{} 其實也沒差 如果使用者寫 A* p =new A; 因為virtual ~A()=0 他也會compile error說 abstract class不給你產生instance 所以其實我在外面寫A::~A(){} 是正確的做法沒有關係??? 不太懂 正統的作法該怎麼寫耶 謝謝 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 219.87.64.222
tinlans:就算是 pure virtual destructor 也是需要一份定義。 05/18 15:08
QQ29:我以為~B就是~A的定義耶 跟override類似? 05/18 15:09
tinlans:destructor 不能被 override,所以規定要給定義。 05/18 15:10
tinlans:宣告就照樣寫 = 0,定義就放空的就好了。 05/18 15:10
tinlans:我常開一個檔案,或是在某個檔案裡專門用註解劃一區出來擺 05/18 15:13
tinlans:這些空定義。 05/18 15:13
> -------------------------------------------------------------------------- < 作者: littleshan (我要加入劍道社!) 看板: C_and_CPP 標題: Re: [問題] pure virtual destructor 怎宣告? 時間: Tue May 18 15:57:30 2010 ※ 引述《QQ29 (我愛阿蓉)》之銘言: : 有其他class會繼承A : 其實我code中 class是寫interface 其實就是struct : 但他的destructor不給我宣告成pure : 他一定要我定義 : 所以一定要改成 virtual ~A(){}... : 不然就是 外頭在 定義一次A::~A(){} : 但我覺得很沒道理 interface不就是包一堆pure virtual function 給別人繼承後 : override嗎? : 還是其實在外面寫個空的destructor{} 其實也沒差 : 如果使用者寫 A* p =new A; : 因為virtual ~A()=0 他也會compile error說 abstract class不給你產生instance : 所以其實我在外面寫A::~A(){} 是正確的做法沒有關係??? : 不太懂 正統的作法該怎麼寫耶 : 謝謝 又到了講觀念的時候~ Destructor 和 member function 有極大的不同,首先,你並不會「主動」呼叫 dtor,它總是由 compiler 在物件脫離它的 scope 時,或是使用者呼叫 delete 時, 由 compiler 自動幫你呼叫的。(儘管你可以手動呼叫 dtor,但請不要做這種事, 問題會出乎你意料地多) dtor 負責終結一個物件、送回它所占用的資源。只要 class 有定義 dtor,那麼它 一定要在物件生命結束時被呼叫,否則會產生各式各樣的資源洩露。 試想一下你寫了這樣一個 class: class Base { public: Base(size_t n) : my_data(new int[n]) {} ~Base() { delete [] my_data; } private: int* my_data; }; 這個 class 的意圖很明顯:它在 ctor 中配置了記憶體,同時在 dtor 中釋放, 只要使用者遵守 C++ 的規則 (有 new 就有 delete),基本上可以運作無誤。 (其實我還需要處理一下 copy-ctor 及 copy-assignment,但這不是本文重點) 現在想像一下,如果你有一個 Derived class 要繼承 Base,那你應該怎麼寫 Derived dtor 呢? class Derived : public Base { public: Derived(size_t n) : Base(n), my_derived_data(new char[n]) {} ~Derived() { // 這邊要怎麼寫? } private: char* my_derived_data; }; 我們來看幾件一定要知道的事: * 首先,你一定要寫 ~Derived()。因為 Derived 有可能有自己的成員 (在此例 中即為 my_derived_data),而 Base 對 Derived 一無所知,所以你不可能在 Base 中去幫 Derived 釋放 my_derived_data。 * 再來,你在 ~Derived() 中,唯一能做的就是釋放 Derived 自己的成員。因為 Base::my_data 被宣告為 private,你不可能動到它。而且若由 Derived class 負責釋放 Base class 的成員,將導致程式難以維護。想像一下你如果在 Base 中增加了一個成員,難道要去檢查並修改所有繼承它的 class 嗎?這會是天大 災難。 * 所以你可能會這樣寫: Derived::~Derived() { delete [] my_derived_data; ~Base(); // 呼叫 Base dtor 以釋放 Base::my_data } 這樣就解決了上面說的兩個問題,只要 ~Base() 有好好清理他自己的 member, 那麼在 ~Derived() 中,清理完自己的 member 後呼叫 ~Base() 即可順利清理 整個物件。 不過你會發現,只要你繼承了某個物件,你一定要在 dtor 中呼叫爸爸的 dtor, 才能確保爸爸的遺產(?)都有被正確清理掉。這件事是重覆性的工作,而且很容 易被 programmer 忘掉。所以 C++ compiler 幫你做了一件事: Compiler 會自動在所有 dtor 的結尾處,加入對 base class dtor 的呼叫。 (當然,如果這個 class 沒繼承,就不會做這件事了。) 所以你的 ~Derived 只需要一行: Derived::~Derived() { delete my_derived_data; // compiler 會在這自動呼叫 ~Base() 以清理爸爸的遺產 } 寫到這邊,你應該知道為什麼 base class dtor 不可以是 pure-virtual 了。 如果它是 pure-virtual,表示 compiler 在 ~Derived() 中插入對 ~Base() 的呼叫, 將會變成非法動作。 回到你的例子: class A { public: ........一些pure virtual function virtual ~A() {}; }; ~A() 就放個空定義即可。 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 140.112.29.108
VictorTom:推~~~~~~~~~~~~ 05/18 16:24
tinlans:還是可以宣告成 pure,用來表示那個 class 是 abstract。 05/18 16:46
tinlans:只是定義還是得提供一份就是了。 05/18 16:46
tinlans:要造 polymorphic class 又找不到 method 可以放 virtual 05/18 16:46
tinlans:,一般都是拿 destructor 來開刀。由 pure / non-pure 05/18 16:47
tinlans:來決定這個 class 能不能有 instance。 05/18 16:47
QQ29:謝謝l大 勞煩您 這麼一大篇 = = thx 05/18 17:16
littleshan:啊,感謝二樓補充,我看過的code不多所以很少見到這種 05/18 17:30
loveme00835:推一個~ 05/18 17:55
QQ29:l大你竟然說看得不多...卻有如此造詣...太誇張了.... 05/18 17:59
loveme00835:剛好發現Effective C++ Item 14 剛好也有講這件事 05/18 18:46
lwecloud:推~侯捷的書也會特別去提這點 05/18 19:52
netio:那如果Derived沒call Base的ctor不就... 05/18 20:06
holymars:給樓上的 那不就會call default ctor嗎.. 05/18 20:07
holymars:如果沒有default ctor可以call,根本就不會過XD 05/18 20:08
netio:剛試了把Base(n)拿掉確實不給過XD 05/18 20:32
tomap41017:感謝一大篇辛苦了!!!!!!!!!!! 05/18 22:44
> -------------------------------------------------------------------------- < 作者: QQ29 (我愛阿蓉) 看板: C_and_CPP 標題: Re: [問題] pure virtual destructor 怎宣告? 時間: Wed May 19 01:39:59 2010 L大說的其實我是知道 先問幾個問題~如下 ※ 引述《littleshan (我要加入劍道社!)》之銘言: : ※ 引述《QQ29 (我愛阿蓉)》之銘言: : * 所以你可能會這樣寫: : Derived::~Derived() : { : delete [] my_derived_data; : ~Base(); // 呼叫 Base dtor 以釋放 Base::my_data : } 手動加的話 那compiler會在幫你加嗎? 變成清兩次...我想是不會.... 因為我用VC手動寫 他不讓我呼叫~Base(); unary '~' : 'Base' does not define this operator or a conversion to a type acceptable to the predefined operator 改成Base::~Base();即可 差別在哪....?? 但竟然變成呼叫兩次 ~Base了 造成double free... 所以結論應該是不要雞婆手動亂加嚕? 我問這篇 原本是覺得 如果Base *p = new Derived; p->某A::的function(); <====ok dynamic binding 如果B有override就跑B 所以我以這樣的想法去想 delete p; 類似p->~Base(); 發現兒子有override 就去跑~Derived (我以為~Derived()就算是~Base的override) 但原文的推文T大也說不是.. 因為我把delete p; 呼叫~Base 因為兒子有override所以就去跑 ~Derived 這個行為 想成一般多型的感覺.... 而我又覺得說 我都已經是interface了 如果又加個空定義 好像就不算是pure virtual了.... 才這樣問一下~! 所以其實~Derived()照L大之前那篇文章 他只是蓋掉virtual table紀錄的位址把&~Base 改成&~Derived嗎? 另外藉此問一下 既然Base的private 我Derived繼承下來卻用不到 真要存取 還要寫成 A::PublicFunc(); 然後就算存取了 也沒有啥用處....真要用就改成protected阿 我就在想 C++繼承下來 為啥要連同private都給繼承了 意思大概就是 sizeof(Base)+sizeof(Deived)-sizeof(所有Base::private) 這樣不是多佔了些記憶體空間 又沒啥意義嗎? 還是說Derived有什麼設計 是非得在 Base寫private 不然何必要繼承給Derived呢.... 謝謝.... -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 123.192.105.173
holymars:..你這樣從Derived up-casting成Base時會發生什麼事.. 05/19 02:20
holymars:請求大宇宙的意志把你丟掉的那塊private送回來嗎 05/19 02:20
holymars:繼承的意思是is-a或has-a,不是is-some-part-of 05/19 02:22
holymars:怎麼能只繼承一半啊XDD 05/19 02:22
> -------------------------------------------------------------------------- < 作者: tinlans ( ) 看板: C_and_CPP 標題: Re: [問題] pure virtual destructor 怎宣告? 時間: Wed May 19 02:36:41 2010 ※ 引述《QQ29 (我愛阿蓉)》之銘言: : L大說的其實我是知道 先問幾個問題~如下 : ※ 引述《littleshan (我要加入劍道社!)》之銘言: : : * 所以你可能會這樣寫: : : Derived::~Derived() : : { : : delete [] my_derived_data; : : ~Base(); // 呼叫 Base dtor 以釋放 Base::my_data : : } : 手動加的話 那compiler會在幫你加嗎? 變成清兩次...我想是不會.... : 因為我用VC手動寫 他不讓我呼叫~Base(); 那只是說明用的, 如果看相關的書, 書上也會告訴你那只是寫來講解 compiler 行為用的 pseudo code。 自己加沒有意義也不對。 一般會明確呼叫 instance 的 destructor, 是你想自己做記憶體管理的時候, 通常那附近還看得到 placement new 的 code。 : unary '~' : 'Base' does not define this operator or a conversion to a : type acceptable to the predefined operator : 改成Base::~Base();即可 差別在哪....?? : 但竟然變成呼叫兩次 ~Base了 造成double free... : 所以結論應該是不要雞婆手動亂加嚕? 一般會寫成 this->~Base(); 來呼叫, 這只是語法規則就不多做解釋。 至於結論當然是不要手動亂加。 : 我問這篇 原本是覺得 : 如果Base *p = new Derived; : p->某A::的function(); <====ok dynamic binding 如果B有override就跑B : 所以我以這樣的想法去想 : delete p; : 類似p->~Base(); 發現兒子有override 就去跑~Derived : (我以為~Derived()就算是~Base的override) 但原文的推文T大也說不是.. : 因為我把delete p; 呼叫~Base 因為兒子有override所以就去跑 ~Derived 名稱不同當然不算 override, 但是 C++ 還是給了它 dynamic binding 的特性。 用多型的方式呼叫 base 的 virtual destructor, 還是會根據 instance 內的 vptr 欄位找到屬於它實際型別的 vtable, 然後把裡面的 destructor 拿來 call。 你可以想像 destructor 在 vtable 裡有一個特定的索引值, 假設叫做 DTOR。 不管 destuctor 名字叫 ~Base 還是叫 ~Derived, 都是去抓 vtable[DTOR] 的值。 這樣想你就不會搞得亂七八糟。 Base *p = new Derived 這個動作就註定了 *p 的 vptr 指向 Derived 的 vtable, p->~Base() 當然是抓 Derived vtable 裡面的 destructor 來跑, 所以 ~Derived() 會先跑起來, 再根據上篇講的那個規則 implicit 呼叫 ~Base()。 : 這個行為 想成一般多型的感覺.... : 而我又覺得說 我都已經是interface了 如果又加個空定義 : 好像就不算是pure virtual了.... : 才這樣問一下~! : 所以其實~Derived()照L大之前那篇文章 : 他只是蓋掉virtual table紀錄的位址把&~Base 改成&~Derived嗎? pure virtual 就是「宣告式」後面補 = 0 就算, 不管你有沒有給定義都一樣。 這個 class 只要裡面有 virtual function 標了那個 = 0, 不管該 function 有沒有被被實作, 你這個 class 一定不能產生 instance, 而繼承它的 class 不 override 它也一樣不能產生 instance。 總而言之 compiler 決定事情常常是看「宣告」就定案了。 要求 identifier 在使用前必須先 declare 的語言, 大都是希望能從 programmer 給的宣告中得到最大的提示, 所以宣告要慎重不能亂宣。 給 pure virtual function 提供定義式, 除了 virtual destructor 這種特例之外, 也能提供一個預設實作。 但是這份預設實作必須在 derived class 裡用 explicit call 的方法呼叫, 並不會自動繼承上去。 : 另外藉此問一下 : 既然Base的private : 我Derived繼承下來卻用不到 : 真要存取 還要寫成 A::PublicFunc(); : 然後就算存取了 也沒有啥用處....真要用就改成protected阿 : 我就在想 C++繼承下來 為啥要連同private都給繼承了 : 意思大概就是 sizeof(Base)+sizeof(Deived)-sizeof(所有Base::private) : 這樣不是多佔了些記憶體空間 又沒啥意義嗎? : 還是說Derived有什麼設計 是非得在 Base寫private : 不然何必要繼承給Derived呢.... : 謝謝.... 聽不太懂這問題。 但是如果你的 base 有一個大 function, 你覺得它太大了所以整理進兩個 function, 原本的大 function 變成了: void Base::bigMethod() { foo(); bar(); } 想當然爾你的 Base::foo() 和 Base::bar() 都不算是這個 class 的對外介面, 它的兒子也沒有必要知道 foo() 跟 bar() 的存在, 所以理所當然是把 foo() 跟 bar() 給 private 起來。 同理如果你講的是 data member, 可能你的 Base class 有一個查詢用的函式需要做一些運算, 但是你知道它運算過程中用到的變數在程式結束前不會再變動, 那你可能會寫一個 cache 機制避免重複運算: Base::Base() : initialized_(false), cachedResult_() { } Result Base::calculate() { if(initialized_) return cachedResult_; cachedResult_ = internalCalculate(); initialized_ = true; } 你當然不會認為 initialized_ 和 cachedResult_ 需要被兒子知道它們的存在, 所以也是 private 起來。 這些 private 性質被繼承後還是 private 更是理所當然, 而且 private 本來就是這樣用的。 你說「Derived繼承下來卻用不到」, 但是最大的問題在於的 Derived 根本也不需要用到, 除非是 class 設計不良才可能造成你會想去用。 -- Ling-hua Tseng (uranus@tinlans.org) Department of Computer Science, National Tsing-Hua University Interesting: C++, Compiler, PL/PD, OS, VM, Large-scale software design Researching: Software pipelining for VLIW architectures Homepage: http://www.tinlans.org -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 118.160.105.229 ※ 編輯: tinlans 來自: 118.160.105.229 (05/19 02:37)
nowar100:這串thread還不錯,收錄精華區 05/19 02:44