使用内联函数的一个问题
2019獨角獸企業重金招聘Python工程師標準>>>
最近碰到一個與內聯方法有關的編譯問題,記敘如下。
問題背景
類Scheduler的實現如下所示,其中方法SetStates()僅僅被類本身使用(暫且先不管它的public屬性)。
// scheduler.hpp class Scheduler {public:Scheduler():m_state1(0),m_state2(0) {}~Scheduler() {}inline void SetStates(int state1, int state2);int GetState1() {return m_state1;}int GetState2() {return m_state2;}private:int m_state1;int m_state2; }; // scheduler.cpp void Scheduler::SetStates(int state1, int state2) {m_state1 = state1;m_state2 = state2; }如上代碼構建正常。之后,新建一個新的類SchedulerMgmt,并且在其中使用了類Scheduler當中的SetStates()方法:
// SchedulerMgmt.cpp void SchedulerMgmt::UpdateSchedulerState(int state1, int state2) {m_scheduler.SetStates(state1, state2); }重新編譯,提示錯誤:
scheduler.hpp:10: warning: inline function ‘void Scheduler::SetStates(int, int)’ used but never defined /tmp/ccsOhsNk.o: In function `SchedulerMgmt::UpdateSchedulerState(int, int)': scheduler-mgmt.cpp:(.text+0x111): undefined reference to `Scheduler::SetStates(int, int)' collect2: ld returned 1 exit status由于編譯錯誤的產生僅僅是在新增加類SchedulerMgmt,并在其中調用了Scheduler中的SetStates()之后才產生,同時該方法恰好聲明為內聯方法,很自然的便懷疑到這個方法的聲明之上:inline。于是先后嘗試了下面兩種方法來排錯:
- A: 在scheduler.cpp當中為SetScheduleParams方法的實現加上"inline"關鍵字。
- B: 去掉scheduler.hpp當中SetScheduleParams方法的“inline”聲明。
測試的結果是第一種方法A不管用,編譯錯誤依然存在,方法B解決了問題。
這樣的結果讓自己感到疑惑不已。眾所周知,將方法聲明為內聯的方式有兩種,其一為在class的定義當中定義成員方法;其二是使用inline關鍵字。第一種方式經過測試是可行的,然而為什么第二種方式并沒有達到目的呢?難道問題出在外部調用內聯方法上面?這是第一個疑惑。
方法B在將SetStates()的內聯屬性去掉問題消失,這是不難理解的。但當我僅僅在其定義上加上inline修飾符,便又會出現問題,只是這個時候僅僅剩下鏈接的問題:
/tmp/ccoizmZA.o: In function `SchedulerMgmt::UpdateSchedulerState(int, int)': scheduler-mgmt.cpp:(.text+0x111): undefined reference to `Scheduler::SetStates(int, int)' collect2: ld returned 1 exit status這是另外一個疑惑。看來對于內聯的許多細節,自己之前并未注意到。
內聯方法的基本要求
在查閱了C++標準最新的Draft之后,從中找到了針對“方法A沒有解決編譯錯誤”的解釋:
An inline function shall be defined in every translation unit in which it is odr-used and shall have exactly the same definition in every case (3.2). [ Note: A call to the inline function may be encountered before its definition appears in the translation unit. —end note ].
這里提到內聯函數的基本要求:任一調用該函數的地方均需要看到它的定義(這一點上,C++中的template也有這樣的要求)。所以在每一個編譯單元都需要定義(注:在程序的構建工程當中,編譯階段會對每一個源代碼文件分別編譯、匯編,之后將之后的輸出文件進行鏈接等處理,這里的編譯單元可以看做是一個個獨立的源代碼文件。)可見,將內聯成員方法定義在類的定義里面是最為穩妥的。在方法A當中,類SchedulerMgmt中盡管可以看到SetStates()聲明為inline,但是卻無法見著它的定義,便提示錯誤。這即是如上第一個疑惑的解答案。
編譯器對于內聯方法處理上的差異
對于第二個疑惑,其實可以歸結于一點:編譯階段對于類中的普通成員方法與內聯成員方法的處理有差異,但差異在什么地方?要回答這個問題,可以從編譯之后的結果上去看。
使用objdump -s -d scheduler.o分別查看輸出修改前后的scheduler.cpp編譯之后的匯編碼:
可見,在將SetStates()定義為內聯方法時,在編譯之后的目標文件中并不會包含該函數的指令,也就是說編譯器將一個方法內聯之后,并不會在目標文件當中將其保留,就如同宏在預處理階段直接文本擴展一樣。因此,到這里第二個疑惑解開。
上面是筆記的主要內容。其實在參考資料<sup>2</sup>鏈接的一篇文章中介紹了有關extern inline的知識,權因本筆記是對工作中問題的一次小總結,所以不再將其納入討論。
編譯器:
g++ (GCC) 4.1.2 20080704 (Red Hat 4.1.2-48) Copyright (C) 2006 Free Software Foundation, Inc.
參考:
轉載于:https://my.oschina.net/peter87/blog/517305
總結
以上是生活随笔為你收集整理的使用内联函数的一个问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android Notification
- 下一篇: FullPage.js