作者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 設計不良才可能造成你會想去用。
在純 C 的年代講究命名空間乾淨又封裝良好的話,
老師應該都會教你沒事就應該把 function 給 static 起來,
你真的確定要重複使用的才把它的 static 拿掉,
並把它的宣告整理在 header file,
這樣其它編譯單元用了同名函式就不會衝到名稱。
icc 的 warning 在這方面也做得不錯。
多了 class 以後封裝概念從編譯單元縮小到 class level,
沒事加 static 的概念跟著縮小到 class level 就變成了 private,
C -> C++ 差不多就是這種封裝概念轉換,
這件事最好把它當成常識。
--
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:47)
推 nowar100:這串thread還不錯,收錄精華區 05/19 02:44
推 loveme00835:有時候多思考 "HOW" 會比 "WHY" 更快理出結果, 就好像 05/19 04:18
→ loveme00835:copy ctor 規定只能傳 reference 一樣 05/19 04:18
推 VictorTom:推:) 05/19 09:25
→ tinlans:copy ctor 只能傳 ref 是因為會造成無限 copy 遞迴發生。 05/19 12:06
→ tinlans:這種事情還是講 why 比較容易理解,不然就像死背了。 05/19 12:06
→ loveme00835:還是希望編譯能過, 當一遍就能知道結果了 05/19 16:28
→ loveme00835: ^為什麼 05/19 16:30
→ loveme00835:不然沒有深刻體會, 還是會一直問為什麼 05/19 16:34
→ tinlans:我比較怕的是,編不過或當掉以後,改用奇怪又有問題的方法 05/19 18:06
→ tinlans:去解,時間再往後推,變成那個人去教別人,結果把亂七八糟 05/19 18:07
→ tinlans:的解法教給下一個人。最慘的是他還回在 google 搜尋得到的 05/19 18:07
→ tinlans:forum 或 blog 上,那真的是害人無數。 05/19 18:08
→ tinlans:因為遇過太多次這種狀況了,乾脆一次把預防針打完。 05/19 18:09
→ loveme00835:說的也是XD 05/19 19:42
推 revivalworld:推 XD 05/20 06:44