C++ 中 cout 是個對象,包含頭文件後可以直接用,那麼它是在哪裡定義的呢?
只要包含了頭文件 &
cout &<&< "Hi there" &<&< endl;有的朋友說是在iostream定義的,可是我看iostream只是聲明,extern ostream cout;再說了,如果在頭文件定義一個對象,那很多編譯單元都包含這個頭文件,那豈不是重複定義這個對象了? 另外,如果在某處預先定義了,那就說明寫任何程序前,這個對象已經存在了?再繼續推理,豈不是我們沒寫任何程序前,會有很多類似cout 的對象都已經存在了?再加上using namespace std這個編譯指令就可以直接用,比如: 不懂,求解答~
Linux/GCC 是在 libstdc++.so ,你可以用 ldd a.out 看看,C++ 程序會鏈接這個動態庫。
看一堆說定義在頭文件的真是看的尷尬。。。
你要明白全局對象的鏈接原理,這個是繼承自C的,每個cpp當中定義的全局變數(也包括C++中對象類型的變數)在編譯的時候和函數一樣會生成符號,這些符號也參與鏈接,當你在代碼中用extern聲明了這個全局變數的時候就可以鏈接到這些符號,跟函數一樣的。所以關鍵在於你在某個cpp當中定義了這個全局變數。對於系統庫來說自然是在系統庫的cpp裡面,一般來說你只裝系統庫的頭文件,所以自然看不到那個定義。
各種 crt 的某個 .cpp/.cc 文件裡面,實現成了一個輸出到 stdout 的 ostreamlibcxx 的是在這:libcxx/iostream.cpp at master · llvm-mirror/libcxx · GitHub
還有許許多多的東西你一般看不到定義,只能看到聲明,但大家天天會用。比如printf,比如CreateWindowW。這些東西是定義在庫中的。沒有源碼也可以用。printf位於C的runtime庫中,cout則是C++的runtime庫,CreateWindowW則是Win32 API庫。
頭文件中是不能定義對象的,如果定義了,會造成包含這個頭文件的多個源代碼文件產生命名衝突,這你說的對。頭文件中的cout只是聲明。定義是在庫的二進位文件中。
定義在庫中的cout只是一個「定義」,要有一個C++程序執行起來,程序中才會有符合這個定義的對象。討論對象的各項屬性,只有在運行時刻才有意義。
庫中定義的對象有多少份,要分靜態庫與動態庫來討論。如果是變數對象,那麼每個程序都會有一份,不分靜態與動態庫。如果是函數對象,靜態庫仍然是每個程序一份,但動態庫在整個系統中只會有一份。libc++里cout/cin/cerr定義在iostream.cpplibcxx/iostream.cpp at 2f3771acf0a28c15f2b892cef38360dbdfe0b8bb · llvm-mirror/libcxx · GitHub
加點料。因為題主一開始的問題並沒有這麼清楚,只是問在哪裡定義。當時手機輸入並且在回復一個政治議題(字有點多),就隨便回答一下。從原問題的描述看,我以為題主是新手不明白「聲明」和「定義」的定義,就想當然的亂用"定義"這個詞來回答圖省事,是我不對,舊答案是錯誤的。我舊答案的原意是一旦#include &
正確的答案是:在iostream中聲明,哪裡定義未知(標準未強求),但一旦#include &
#include &
namespace std {
//在std中聲明任何東西都是undefined behavior,不要學,這裡只是舉例
extern ostream cout;
}
struct Test {
Test() { std::cout &<&< "abc"; }
};
//opps, undefined behavior, 這裡是undefined behavior的最根本原因是C++標準認為,
//沒有#include &
//當然不同的編譯器實現可能會有不同的表現。
Test t;
//晚了
#include &
int main() { return 0; }
在gcc中編譯成功,運行coredump(segmentation fault)。
clang+libstdc++編譯成功,運行coredump(segmentation fault)。clang+libc++編譯成功,link不成功。都符合undefine behavior標準。但是下面就沒問題,
#include &
namespace std {
//在std中聲明任何東西都是undefined behavior,不要學,這裡只是舉例
extern ostream cout;
}
struct Test {
Test() { std::cout &<&< "abc"; }
};
#include &
Test t;
int main() { return 0; }
雖然是undefine behavior, 其實無傷大雅,gcc clang順利運行。
clang+libc++仍然通不過link。也不違背標準。理論上std namespace不允許被聲明定義任何東西。如果不考慮「定義」的標準定義,把「定義」理解成「constructed「或初始化,理論上就相當於cout在iostream中被「定義」(constructed&initialized)了。這也是我舊答案的原意。
摟主原問題是「那就說明寫任何程序前,這個對象已經存在了?「,沒有被constructed的或者初始化的對象不能算成對象,所以說不存在。不include &
查下標準,實際上比我記憶中的更複雜,以下為標準原話:
The objects are constructed and the associations are established at some time prior to or during the first time an object of class ios_base::Init is constructed, and in any case before the body of main begins execution. The objects are not destroyed during program execution.
cout會在 ios_base::Init的對象第一次被constructed的時候 被constructed。
The results of including &
in a translation unit shall be as if & defined an instance of ios_base::Init with static storage duration. Similarly, the entire program shall behave as if there were at least one instance of ios_base::Init with static storage duration.
#include &
-----------------------------------以下原答案--------------------------
#include &一般來說是在 libstdc++ 或者 msvcrtXX 裡面
推薦閱讀:
※學習c++多線程編程主要用pthread還是c++11中的thread類?
※Boost.Asio成為C++標準庫一部分的機會大嗎?
※為什麼C/C++的預處理指令#include不自動讓所包含的文件只包含一次?
※gcc編譯大文件非常慢,是有什麼限制嗎?
※為何C++/Rust都不允許靜態函數是虛的?
TAG:C |