Include Cpp?還可以這樣?
本文轉(zhuǎn)載自微信公眾號(hào)「程序喵大人」,作者程序喵大人 。轉(zhuǎn)載本文請(qǐng)聯(lián)系程序喵大人公眾號(hào)。
前兩天突然看見(jiàn)部門有個(gè)項(xiàng)目的代碼里通篇全是#include "xxx.cpp",我表示從來(lái)沒(méi)見(jiàn)過(guò)這種寫法,引發(fā)了我的一些思考:
問(wèn)題一:這啥玩意?
C++是一門高深莫測(cè)的語(yǔ)言,什么寫法都有,而且#include本質(zhì)上就是復(fù)制粘貼代碼,我也不敢說(shuō)別人寫的不對(duì),可能開(kāi)發(fā)者是C++大佬,寫了一些我們普通人無(wú)法理解的代碼也是正常的。
問(wèn)題二:整個(gè)項(xiàng)目都是這種引用方式,不會(huì)導(dǎo)致某一函數(shù)重復(fù)定義嗎?
為此我查了一些資料,并做了一些測(cè)試:
代碼段1:
- // file1.cc
- #include <iostream>
- using std::cout;
- void ddd() { cout << "ddd \n"; }
代碼段2:
- // file2.cc
- #include "file1.cc"
- int main() {
- ddd();
- return 0;
- }
代碼段3:
- // filec.cc
- #include "file1.cc"
- void f() {
- ddd();
- }
然后三個(gè)源文件一起編譯鏈接:
發(fā)現(xiàn)報(bào)錯(cuò)了,的確出現(xiàn)了multiple definition的錯(cuò)誤,確實(shí)一個(gè)函數(shù)不能有多個(gè)定義。我又改了下代碼:
- // file1.cc
- #include <iostream>
- using std::cout;
- inline void ddd() { cout << "ddd \n"; }
將ddd函數(shù)改成了內(nèi)聯(lián)函數(shù),然后三個(gè)源文件一起編譯鏈接:
編譯成功且正常輸出。
我將普通函數(shù)改成成員函數(shù)又測(cè)試了一次:
代碼段1:
- file1.cc
- #include <iostream>
- using std::cout;
- struct A {
- int a_;
- void func();
- };
- void A::func() { cout << "file1.cc a " << a_ << "\n"; }
代碼段2:
- // file2.cc
- #include "file1.cc"
- int main() {
- A a;
- a.func();
- return 0;
- }
代碼段3:
- // filec.cc
- #include "file1.cc"
- void f() {
- A a;
- a.func();
- }
然后一起編譯鏈接:
發(fā)現(xiàn)成員函數(shù)這樣定義也會(huì)報(bào)錯(cuò),也會(huì)有multiple definition的錯(cuò)誤,我又改了一下代碼:
- // file1.cc
- #include <iostream>
- using std::cout;
- struct A {
- int a_;
- void func() { cout << "file1.cc a " << a_ << "\n"; }
- };
將函數(shù)的定義搬運(yùn)到了類中,編譯鏈接:
程序正常運(yùn)行,熟悉C++的朋友可能都知道原因,類中定義的函數(shù)就相當(dāng)于是內(nèi)聯(lián)函數(shù),所以編譯鏈接不會(huì)有問(wèn)題。
所以得出結(jié)論:
- 內(nèi)聯(lián)函數(shù)的定義可以被多個(gè)源文件引入(內(nèi)聯(lián)函數(shù)到最后其實(shí)不是個(gè)函數(shù))
- 類的定義可以被多個(gè)源文件引入(這是必須的,要不然編譯器怎么知道類的對(duì)象布局)
問(wèn)題三:貌似平時(shí)使用的模板就多數(shù)都定義在頭文件中,這個(gè)不會(huì)導(dǎo)致某一函數(shù)重復(fù)定義嗎?
直接看三段代碼吧:
代碼段1:
- // temp.h
- #include <iostream>
- template <typename T>
- struct B {
- T a;
- void ff() { std::cout << "temph \n"; }
- };
代碼段2:
- // filec.cc
- #include "temp.h"
- void f() {
- B<int> a;
- a.ff();
- }
代碼段3:
- // file2.cc
- #include "temp.h"
- int main() {
- B<int> a;
- a.ff();
- return 0;
- }
所有源文件編譯鏈接:
發(fā)現(xiàn)編譯成功且正常運(yùn)行,那如果函數(shù)的定義不在類內(nèi)會(huì)怎么樣呢?
- // temp.h
- #include <iostream>
- template <typename T>
- struct B {
- T a;
- void ff();
- };
- template <typename T>
- void B<T>::ff() {
- std::cout << "temph \n";
- }
程序編譯鏈接后:
編譯鏈接成功且輸出正常結(jié)果。
所以得出結(jié)論:編譯器對(duì)模板做了特殊處理,不論模板類中函數(shù)是否內(nèi)聯(lián),都可以正常鏈接。
這個(gè)結(jié)論其實(shí)不是我得出的(所以可信),而是gnu文檔(參考資料的最后一個(gè)鏈接)寫的,上述代碼只是為了印證結(jié)論。
大體意思如下:編譯器對(duì)模板做了特殊處理,如果函數(shù)不是內(nèi)聯(lián)函數(shù),那可以有兩種處理方式:
- 鏈接時(shí)隨機(jī)選擇一個(gè)定義,其它的丟棄掉
- 編譯器會(huì)把函數(shù)的定義單獨(dú)提出來(lái),提到單獨(dú)一個(gè)文件中,對(duì)此文件單獨(dú)編譯,就不會(huì)出現(xiàn)重復(fù)定義的問(wèn)題。
搞定,大家對(duì)此還有什么問(wèn)題,歡迎留言!
參考資料
https://zybuluo.com/uuprince/note/81709
https://stackoverflow.com/questions/15866258/template-class-multiple-definition
https://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html