看板 C_and_CPP 關於我們 聯絡資訊
先不說把 * 藏起來的問題。 如果你在寫 C++ 的話, 這類 typedef 其實是一個非常麻煩的東西。 因為... --------------------------------------------------- // node.hxx typedef struct node { int value; struct node *prev; struct node *next; } Node; void NodeAPI1(Node *); void NodeAPI2(Node *, Node *); ... --------------------------------------------------- // node_client.hxx struct Node; void foo(Node *); ... --------------------------------------------------- // node_client.cxx #include "node.hxx" #include "node_client.hxx" void foo(Node *n) { // 這邊會存取到 n->value 等內部欄位 } ... --------------------------------------------------- 編譯後得到錯誤訊息: In file included from node_client.cxx:4: node_client.hxx:3: error: using typedef-name 'Node' after 'struct' node.hxx:7: error: 'Node' has a previous declaration here 調換 node_client.cxx 兩行 #include 順序的話: In file included from node_client.cxx:4: node.hxx:7: error: conflicting declaration 'typedef struct node Node' node_client.hxx:3: error: 'struct Node' has a previous declaration as 'struct Node' 一種解決方案是拔掉 node_client.hxx 的前置宣告, 然後讓它直接去 #include "node.hxx"。 但這樣做的話就會增加編譯期相依性, 任何寫到 #include "node_client.hxx" 的檔案, 只要 node.hxx 修改就會被重新編譯。 這也是為什麼 node_client.hxx 一開始會打算放前置宣告的原因。 所以 C++ 其實不是很喜歡這種 typedef 的用法, typedef 在 C++ 一般比較常被用在 template 相關的 code 上。 一開始的寫法其實用純 C compiler 可以編譯通過, 不過 GCC 4.5 開始能用 gcc -Wc++-compat 得到警告訊息: In file included from node_client.c:4:0: node_client.hxx:3:8: warning: using 'Node' as both a typedef and a tag is invalid in C++ node.hxx:7:3: note: originally defined here -- 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.107.243 ※ 編輯: tinlans 來自: 118.160.107.243 (10/04 21:18)
manlike:好像搞得很複雜,這樣程式碼很難維護,可讀性也不高... 10/04 21:30
manlike:該include就include吧 犧牲一些compile time還好吧 除非 10/04 21:31
manlike:加了那個前置宣告可以讓你compile time從一天縮短到幾小時 10/04 21:33
manlike:而且你的Makefile也會變得很複雜 還是專注在寫程式好 XD 10/04 21:34
loveme00835:其實編譯時間時嚜的還是其次, 重點是「增加相依性」的 10/04 21:39
manlike:要不然乾脆直接把他做成 share library 只要 link XD 10/04 21:41
loveme00835:問題, 造成維護上的麻煩 10/04 21:41
manlike:本來就是相依的 程式都連在一起 你怎樣做都相依 = ="" 10/04 21:43
manlike:重點就在於編譯時間 是不是每次改動一點就要全部重編 = = 10/04 21:44
manlike:還是只要重編改過的 然後link進去就好 10/04 21:45
manlike:這樣寫維護性才不高 下一個看code的人 很難看懂 10/04 21:47
manlike:要追一下才會知道那個前置宣告到底定義在哪?? node.hxx 10/04 21:48
yoco315:不是吧,重點是根本不該那樣 typedef 吧,就全都可以避免 10/04 21:52
loveme00835:相依性有分強弱, 編譯的時間還可以用防火牆來擋, 為了 10/04 21:52
loveme00835:方便追亂引入, 等到ADL你就知道 10/04 21:53
purpose:C編譯器太鬆了,這樣寫也能通過,在Windows可以用cppcheck 10/04 21:53
purpose:對前制處理後(cl.exe /E)的檔案做靜態程式碼分析抓出問題 10/04 21:54
purpose:會回報Struct 'Node' hides typedef with same name 10/04 21:54
manlike:loveme00835 ... Orz 10/04 21:55
purpose: 置 10/04 21:55
yoco315:love 大大 Orz 10/04 21:56
loveme00835:@_@ 怎麼了嗎? 囧rz 10/04 22:06
tinlans:會出現前置宣告,除了降低編譯期相依性,也是告訴使用該 10/04 22:07
tinlans:header 的人,不需要知道 Node 細節。只要操作 Node * 10/04 22:07
tinlans:這種不透明的型別,和使用 foo() 這種 API 就可以了。 10/04 22:08
tinlans:如果 node_client.hxx 是很基礎的 utility header,用到它 10/04 22:09
tinlans:的有幾十個 .cxx。要是直接 #include "node.hxx", 10/04 22:10
tinlans:那就是大規模的重編譯。 10/04 22:10
tinlans:↑ 如果動到 node.hxx 的話。 10/04 22:11
loveme00835:所以才會有Pimpl idom這種手法出現, 不過也是少用為妙 10/04 22:33
loveme00835: i 10/04 22:58
purpose:tinlans大最後的推文說明完,比較懂這種設計的考量原因了 10/04 23:02
nowar100:今天花點時間看懂了,推一下 10/05 08:58
xatier:不過ADT好像常常用這種手法= =a 搞的維護者很難看懂程式碼 10/05 12:09