關於 inline 函式的分析: *** undefined reference to ***
阿新 • • 發佈:2019-02-12
- 如果將函式的實現放在標頭檔案中,那麼每一個包含該標頭檔案的cpp檔案都將得到一份關於該函式的定義,那麼連結器會報函式重定義錯誤。
- 如果將函式的實現放在標頭檔案,並且標記為 inline 那麼每一個包含該標頭檔案的cpp檔案都將得到一份關於該函式的定義,並且連結器不會報錯。
- 如果將函式的實現放在cpp檔案中,並且沒有標記為inline,那麼該函式可以被連線到其他編譯單元中。
- 如果將函式的實現放在cpp文件中,並且標記為inline, 那麼該函式對其他編譯單元不可見(類似static的效果),也就是其他cpp檔案不能連結該函式庫,這就是標題中出現的 … undefined reference to …
問題原因就是,編譯器在編譯一個inline函式時,需要知道其完整定義,如果編譯器在本編譯單元找不到inline函式定義就會報錯(inline
所以,將類的成員函式的實現放在標頭檔案中不會出現重定義錯誤,是因為在類中定義成員函式預設為inline函式。
所以下面會報重定義錯誤(當頭檔案被兩個及以上的cpp檔案包含時):
# foo.hpp
class Foo
{
Foo();
};
Foo::Foo() // 實現在標頭檔案中,並且沒標記為inline,會報編譯錯誤!
{
}
但是如下這樣就不會報錯:
# foo.hpp
class Foo
{
Foo() // 預設 inline
{}
};
或者如下這樣也不會報錯:
# foo.hpp
class Foo
{
inline Foo();
};
Foo::Foo() // 實現在標頭檔案中,並且顯示標記為inline
{
}
如果debug的時候,發現自己明明定義了該函式,卻還是報 *** undefined reference to ***
錯誤,那麼可以看一下是不是不小心把cpp檔案中的函式寫成inline了。
不過,將 inline 函式定義在 cpp 檔案中有個好處,正如 《大規模c++程式設計》一書中所說,那就是他擁有 internal linkage 屬性(不會輸出符號到.o檔案中),不會汙染全域性名稱空間(類似static函式):
// file1.cpp
int i // external linkage
int max(int a, int b) {return a > b ? a : b;} // external linkage
// file2.cpp
inline int min(int a, int b) {return a < b ? a : b;} // internal
static int fact(int n) {return n<-1 ? 1 : n * fact(n-1);} // internal
其他同樣不會輸出符號到.o檔案中的有:
// file3.cpp
class Link; // 類宣告
enum { START_SIZE = 1, GROW_FACTOR = 2}; // 列舉定義
const double PI = 3.1415926595;
static Link *s_root_p;
Link * const s_first_p = s_root_p;
typedef int (PointerToFunc *) ();
#define CASE(x) case X: cout << "x" << endl;