Re: [問題] inline和.h寫定義

看板C_and_CPP作者 ( )時間13年前 (2010/09/15 03:53), 編輯推噓16(16010)
留言26則, 17人參與, 最新討論串3/3 (看更多)
※ 引述《QQ29 (我愛阿蓉)》之銘言: : 請教一個問題... : 之前版上看到 : "寫在.h 定義一定是inline"... : 但是我爬文後也找不到相關能佐證的文章... : 請問這正確嗎?? : 如果自己寫inline這修飾字 也只是"建議" inline : 如果定義寫在.h "一定"是inline嗎? 你需要的是類似 UNIX 下一個叫 nm 的工具。 它可以用來查詢 object file 裡有哪些 symbol 以及 symbol 的屬性, 其中 -C 參數還可以做 demangling。 很遺憾的是, 我不知道 MS 平台的這個工具叫什麼名字。 inline 除了建議 compiler 做 inline 外, 它也告訴 compiler 在 inline 失敗時要做對應的防範措施。 GNU 的 C++ compiler 常見的做法就是使用 weak symbol。 用高階語言的角度來說, 它就是允許同一個 identifier 能被重複定義多次的功能。 原本它的用途是讓 library 的實作能被 override; 但是因為 inline 通常由 programmer 保證實作碼一致, 所以每份實作互相 override 對方其實也沒差。 你的 linker 在最後隨便挑任一份實作來用都沒問題。 以這個程式碼來說: inline void foo() { } int main() { foo(); return 0; } 用 g++ -c 編譯成 object file, 再拿 nm 去看可以看到這兩個 symbols: 0000000000000000 W foo() 0000000000000000 T main W 就是說這是一個 weak symbol。 T 是說它位於 text section。 大寫字母代表這是 external symbol, 小寫字母代表這是 local symbol (static function 會變成這東西)。 雖然 compiler 也可以用 local symbol 實現 (從大 W 變成小 t), 但是這樣你最後的執行檔就註定要很肥。 好奇其它字母的意義就自己查手冊吧, 總之有這工具你就可以做很多實驗去看 compiler 的行為。 當然你直接讓 compiler 輸出組語檔來看也行。 這裡不是在教你萬事都要靠實驗, 只是這原本就是語言保留給 compiler 去煩惱的東西。 語言只說 compiler 要自行保證不會出事, 實際上 compiler 怎麼做到就要觀察了 回到正題。 因為你在 header 裡標了 inline, 所以 g++ 才會把那個 function 標成 weak symbol。 所以就算 inline 失敗, 你還是不會在 linking time 收到 multiple defintions 的 error。 不過 weak symbol 這功能也常讓知道它存在的人便宜行事。 譬如被看不懂 linker 錯誤訊息的人拿來解那個 error, 因為 GCC 允許你手動為函式或變數加上 weak 屬性,如: void foo() __attribute__((weak)); 結果就是程式出現非預期行為, 或是他程式交接給別人維護以後常在停車場發現自己的車胎被放氣。 weak symbol 是 ELF 格式的東西, 所以我不清楚 Windows 的 PE 格式有沒有等價物。 你好奇的話可能要自己去摸才知道。 另外補充一點, function template 在 header files 不需要標 inline。 C++ compiler 必須想辦法讓它不會造成 multiple definitions, 而 GCC 的做法也是使用 weak symbol。 : 假如我class一坨private data..... : 大家都會針對每個private data 寫一份get and set function : 所以說有x個private data 就該有 2*x個function? : 感覺這樣好累 但是不是好的設計一定要這樣寫?? 你需要自己寫一個程式產生器。 也許事後你可能會發現, 你的 client code 其實並不需要直接存取這麼多東西。 比方說到處都有這種重複的 code: value1 = obj->get1() * x; value2 = obj->get2() / y; ... result = value1 * value2 + ...; obj->set15(result); 這個時候你就可以把這些 code 搬回 obj 所屬的 class 內, client code 可以變成這樣一行: obj->calculateSomething(x, y); 最後也許你發現這個 class 真正要提供的是 calculateSomething(), 然後那些奇奇怪怪從頭到尾都用不到的 getters/setters 就可以刪了。 如果你一開始是從物件導向分析和設計走過來的, 需要這類重構的機率就會大幅減小。 不過現在大家都不吃預先設計這一套, 所以事後回頭重新整理自己的 code 是必要的。 : 至於這種小function 都寫inline修飾 或直接寫在.h的話 : 不就違反 : .h不要寫定義的精神嗎??? 又不是在 header 寫定義就會被抓去槍斃。 之所以會有這種說法, 是因為要讓人看到你的 class 一眼就明白提供什麼。 你放了一堆以數十行為單位的實作碼在裡面, 這樣別人是要怎麼看? 但是 int getValue() { return value_; } 這種一行的, 其實也沒差了。 還有放在 class 的大括號內自動就是 inline, 你不用另外寫 inline。 : 如果"定義寫在.h一定inline" 成立 : 我把定義寫在.h + lib 的話 : 給對方header file他不就看的出來我一些定義 雖說這些function都是簡單get set... : 我該如何在.h不要寫定義 和 inline做取捨呢? More Effective C++ 這本舊到爛掉的書, 有講過一個叫 80/20 法則的東西。 雖然這是商科那邊的名詞, 不過跟資工這邊學到的 Amdahl's law 是同一本質。 總而言之很多書都會告訴你, inline 這種最佳化不要太早做。 等效能分析器告訴你效能瓶頸出在那邊, 你再考慮把它改成 inline。 這就是取捨的方法。 當然如果只是 getters/setters, 你直接丟 header file 也沒差。 只是當你打算改變它們的行為時, 要重編譯的檔案可能會很多。 常見的一個狀況是, 你可能會在開發期間陸續插入一些 assert() 做檢查。 之所以強調封裝要確實, 也是方便你加入一些檢查, 以及新舊版本實作之間的銜接或相容等等。 inline 基本上是一個最佳化工具, 而不是方便你寫程式的工具。 如果你不是因為效能考量而是方不方便寫, 那麼也就沒有什麼取捨的問題。 你覺得高興就好了。 我的話是會產生分離的宣告和定義, 所以在 class 定義式內看不到 getters/setters 的實作。 因為要關閉 assert() 以 gcc 來說是在編譯選項加 -DNDEBUG, 而這種 Makefile 層的修改要讓它被偵測到會比較麻煩。 這讓我必須重新編譯整個專案, 才能確保 getters/setters 的行為一致。 雖說直接在 header 上 #define NDEBUG 也行, 但就這就要保佑下次 commit source code 之前記得刪掉。 不然可能會有人跑來罵。 : ps. 看教學都是寫 宣告 和 定義 都要寫上inline在前面 如果一邊不寫會怎麼取捨呢? : http://www.greenend.org.uk/rjk/2003/03/inline.html : 這網頁有提到static inline 但光看他描述實在不懂 且為啥他沒寫inline這字眼呢? 這網頁主要是在講 C 語言的 inline。 那是一段黑歷史。 你現階段是以 MS Windows 的 C++ compiler 為主, 所以不用知道也沒差。 用純 C compiler 才有必要知道 inline 和 static inline 在 C99 前後的差別。 這個故事夭壽的長, 所以不想講。 反正你只會用到 inline 就對了, static inline 請無視。 : 我在想 之前在學校都有講概念說 class封裝 可以達到資料 隱藏阿之類的 : 我現在突然無法理解他的涵義 : 我給他.h 他把我private:這字眼全刪掉 改成public: 那不就可以任憑他亂搞嘛? : 我那些get set形同虛設 我記得是 Inside the C++ Object Model 還是哪本書有說, class A { public: int a; int b; inb c; }; 這種東西的 memory layout 會和純 C 這樣寫等價: struct A { public: int a; int b; int c; }; 所以當你混合連結 C 和 C++ 的 object files 或 libraries 時, 你可以在 C++ 程式裡用前者代替後者。 而前者即使擁有 non-virtual member functions, 它們還是完全等價的東西。 但是如果你改成: class A { public: int a; private: int b; int c; }; 書上是說, 不同存取權的 sections 先後順序 compiler 不保證。 雖然現今所有的 compiler 都是 layout 成 a -> b -> c, 但是 b -> c -> a 也是被允許的一種 layout。 只是這種 layout 方式我也從來沒看過就是了。 而且如果全部丟到 public: 下的話, 也不會以這個問題。 但是這些都不重要。 存取權修飾字只是拿來防止 programmer 不小心犯錯, 不是拿來把 programmer 當賊防。 他真的硬要搞的話, 還是可以用 pointer 去改值。 封裝的用途前面也提很多了, 這裡就不再提。 : 如果是 針對 code實作不想被別人看到要隱藏 編成lib.... : 那也不用針對class來講這觀念..... : 但就算編程lib .h一坨private他還是可以直接改成public 在自己來亂搞 : 這觀念在這邊請教一下...... 你忘了 pimpl idiom。 因為它真正負責實作的 class 是被直接定義在實作檔裡, 所以在你做成 lib 後別人就真的完全摸不到。 想做到的話, 幾乎只有直接幹走你的 source code 或反組譯這幾招。 C++ 爸爸寫的聖經本還有提到所謂的內部/外部 header files, 這個方法也是不錯, 應用在 pimpl idiom 上也能讓實作碼不必全擠在一個檔案裡。 我覺得你誤解了資訊隱藏的原意。 它的目的是讓你的同事或使用者「不需要」知道實作細節, 而不是「不可以」知道實作細節。 這完全是兩碼子的事。 -- 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.214 ※ 編輯: tinlans 來自: 118.160.105.214 (09/15 04:34)

09/15 04:45, , 1F
關於第一段 VC 有 dumpbin 可以用
09/15 04:45, 1F

09/15 04:45, , 2F
加上 /symbol 即可查詢 .obj 中的 symbol
09/15 04:45, 2F

09/15 04:49, , 3F
修正 /symbols
09/15 04:49, 3F
※ 編輯: tinlans 來自: 118.160.105.214 (09/15 06:03)

09/15 08:33, , 4F
推推喔 :)
09/15 08:33, 4F

09/15 09:50, , 5F
大推文章跟推文!!
09/15 09:50, 5F

09/15 11:13, , 6F
09/15 11:13, 6F

09/15 11:22, , 7F
推推推
09/15 11:22, 7F

09/15 11:32, , 8F
好文推
09/15 11:32, 8F

09/15 13:34, , 9F
剛剛想到在 C++ Coding Standards 裡的Ch8也有講到該
09/15 13:34, 9F

09/15 13:35, , 10F
避免過早最佳化, 我覺得很多事經典的書上說, 有認真找
09/15 13:35, 10F

09/15 13:36, , 11F
至少一些常見的錯誤都不會出現
09/15 13:36, 11F

09/15 13:36, , 12F
^都有
09/15 13:36, 12F

09/15 13:40, , 13F
好文...推一下
09/15 13:40, 13F

09/15 17:13, , 14F
推!
09/15 17:13, 14F

09/15 21:19, , 15F
每次看完T大的文章都會感動
09/15 21:19, 15F

09/15 21:28, , 16F
推:)
09/15 21:28, 16F

09/15 21:36, , 17F
我每次除了感動外,都還會想T大是陽光男孩型的嗎?(我是男的)
09/15 21:36, 17F

09/15 21:36, , 18F
感覺寫程式的男人最帥了XD
09/15 21:36, 18F

09/15 23:36, , 19F
回樓上,並不是 XD
09/15 23:36, 19F

09/15 23:44, , 20F
tinlans大怕被tylpk...XD
09/15 23:44, 20F

09/16 00:41, , 21F
夠強大呀,一直認為懂系統程式(compiler/linker)的人最猛
09/16 00:41, 21F

09/16 06:11, , 22F
放心啦...我是正常的男人XD 不知道資工的女生也會崇拜嗎?
09/16 06:11, 22F

09/16 06:50, , 23F
樓上幻想得太嚴重了 常逛八卦板應該有助於導正你的觀念 XD
09/16 06:50, 23F

09/16 21:47, , 24F
八卦板導正XDDDDDDDDD
09/16 21:47, 24F

09/18 12:11, , 25F
陽光男孩 XD
09/18 12:11, 25F

09/18 22:41, , 26F
突然想推在一起(拖走...)
09/18 22:41, 26F
文章代碼(AID): #1CZzBMpL (C_and_CPP)
討論串 (同標題文章)
文章代碼(AID): #1CZzBMpL (C_and_CPP)