標籤:

C++ 中 cout 是個對象,包含頭文件後可以直接用,那麼它是在哪裡定義的呢?

只要包含了頭文件 & 再加上using namespace std這個編譯指令就可以直接用,比如:

cout &<&< "Hi there" &<&< endl;

有的朋友說是在iostream定義的,可是我看iostream只是聲明,extern ostream cout;

再說了,如果在頭文件定義一個對象,那很多編譯單元都包含這個頭文件,那豈不是重複定義這個對象了?

另外,如果在某處預先定義了,那就說明寫任何程序前,這個對象已經存在了?再繼續推理,豈不是我們沒寫任何程序前,會有很多類似cout 的對象都已經存在了?

不懂,求解答~


Linux/GCC 是在 libstdc++.so ,你可以用 ldd a.out 看看,C++ 程序會鏈接這個動態庫。


看一堆說定義在頭文件的真是看的尷尬。。。


你要明白全局對象的鏈接原理,這個是繼承自C的,每個cpp當中定義的全局變數(也包括C++中對象類型的變數)在編譯的時候和函數一樣會生成符號,這些符號也參與鏈接,當你在代碼中用extern聲明了這個全局變數的時候就可以鏈接到這些符號,跟函數一樣的。所以關鍵在於你在某個cpp當中定義了這個全局變數。對於系統庫來說自然是在系統庫的cpp裡面,一般來說你只裝系統庫的頭文件,所以自然看不到那個定義。


各種 crt 的某個 .cpp/.cc 文件裡面,實現成了一個輸出到 stdout 的 ostream

libcxx 的是在這: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.cpp

libcxx/iostream.cpp at 2f3771acf0a28c15f2b892cef38360dbdfe0b8bb · llvm-mirror/libcxx · GitHub


加點料。因為題主一開始的問題並沒有這麼清楚,只是問在哪裡定義。當時手機輸入並且在回復一個政治議題(字有點多),就隨便回答一下。從原問題的描述看,我以為題主是新手不明白「聲明」和「定義」的定義,就想當然的亂用"定義"這個詞來回答圖省事,是我不對,舊答案是錯誤的。我舊答案的原意是一旦#include & cout就可以用了,如果沒有#include &就不能用(這還真不是廢話,仔細往下讀)。

正確的答案是:在iostream中聲明,哪裡定義未知(標準未強求),但一旦#include &,就可以看成cout被constructed initialized。沒有include iostream之前,是絕對不能用的(可能沒有被constructed initialized)。見下例。

#include &

namespace std {
//在std中聲明任何東西都是undefined behavior,不要學,這裡只是舉例
extern ostream cout;
}

struct Test {
Test() { std::cout &<&< "abc"; } }; //opps, undefined behavior, 這裡是undefined behavior的最根本原因是C++標準認為, //沒有#include &,cout就可能沒有被constructed initialized。
//當然不同的編譯器實現可能會有不同的表現。
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 &前,cout對象不存在。

查下標準,實際上比我記憶中的更複雜,以下為標準原話:

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 & 相當於(並不強求編譯器如何實現)定義了一個ios_base::Init的對象(static storage duration)。聯繫上面,即#include &就相當於cout,cin等被constructed。

-----------------------------------以下原答案--------------------------

#include &中定義。

如果你沒有#include&,就不會被定義,也不能直接使用。


一般來說是在 libstdc++ 或者 msvcrtXX 裡面


推薦閱讀:

學習c++多線程編程主要用pthread還是c++11中的thread類?
Boost.Asio成為C++標準庫一部分的機會大嗎?
為什麼C/C++的預處理指令#include不自動讓所包含的文件只包含一次?
gcc編譯大文件非常慢,是有什麼限制嗎?
為何C++/Rust都不允許靜態函數是虛的?

TAG:C |