深入理解C++內(nèi)鏈接與外鏈接的意義
上一篇博客給大家解釋了“程序運行鏈接”的概念與意義,并區(qū)分了動態(tài)鏈接庫與靜態(tài)鏈接庫。接下來想和大家談一下C++的內(nèi)鏈接與外鏈接的區(qū)別與意義??赐曛螅M隳芾斫庖韵聨讉€問題~
1. 為什么不要在頭文件中定義具有外部鏈接的實體?
2. 在頭文件中定義具有內(nèi)部鏈接的實體有什么劣勢?
3. 內(nèi)部鏈接與外部鏈接存在的意義是什么?
首先理解什么是編譯單元?
我們知道,其實編譯器在編譯代碼時,只會去編譯.cpp格式的源文件,并且預(yù)編譯器會遞歸的把.cpp所有#include的頭文件都“拷貝”到.cpp文件中去,之后對這個文件再進行編譯,生成二進制的.obj文件。那么其實每一個.cpp文件都是一個編譯單元。
聲明與定義
一個聲明將一個名稱引入一個作用域,C++中在同一個作用域中可以重復(fù)聲明,除了類中的成員函數(shù)與成員變量的聲明。以下都是聲明:
Extern int number; //外部引用聲明 Typedef int int32; // typedef聲明 Class A; //類的前置聲明 Using std::cin; //名字空間引用聲明 Friend f; //友元聲明 Int testFun(); //函數(shù)前置聲明
定義決定了一個實體在一個作用域的唯一描述,同一作用域不可以重復(fù)定義一個實體。以下都是定義:
Int a;
Class Myclass{…};
Myclass ma;
Static int b;
Enum{first, second,third};
Const int m = 2;
Void hello(){…}
什么是內(nèi)部鏈接?
如果一個名稱對于他的編譯單元是局部的,并且在鏈接時不會與其他的編譯單元中同樣的名字沖突,那么這個名稱就擁有內(nèi)部鏈接。這個實體有內(nèi)部鏈接,他就不會與其他.cpp文件同名的實體沖突。換個說法,那些編譯單元(.cpp)中不能向其他編譯單元(.cpp)展示提供其定義的函數(shù)、變量就擁有內(nèi)部鏈接
那么哪些實體擁有內(nèi)部鏈接?
1. 靜態(tài)(static)全局變量,自由函數(shù),友元函數(shù)定義
2. 類的定義
3. 內(nèi)聯(lián)函數(shù)定義
4. Union共用體定義
5. 名字空間的const常量定義
6. 枚舉類型定義
7. 所有的聲明(有人將聲明歸結(jié)為無鏈接)
什么是外部鏈接?
一個多文件的程序中,一個實體可以在鏈接時與其他編譯單元交互,那么這個實體就擁有外部鏈接。
換個說法,那些編譯單元(.cpp)中能想其他編譯單元(.cpp)提供其定義,讓其他編譯單元(.cpp)使用的函數(shù)、變量就擁有外部鏈接
那么哪些實體擁有外部鏈接?
1. 類的非內(nèi)聯(lián)函數(shù)(包括成員函數(shù)和靜態(tài)類成員函數(shù))的定義
2. 類的靜態(tài)成員變量的定義
3. 名字空間或全局的非靜態(tài)的自由函數(shù),非靜態(tài)變量,非友元函數(shù)的定義
那么這里總結(jié)一下,定義這樣的內(nèi)鏈接與外鏈接有什么意義?
所謂鏈接,就是因為項目工程的不斷擴大,寫在一個.cpp文件即難以維護,又不好去合作開發(fā)。所以去將代碼按照比較有條理的,分成多個文件,讓其可以獨立編譯,在最后運行在整合到一起,也就是通過鏈接再去找到需要的代碼。這時候就需要外鏈接定位到合適的代碼。
比如我們定義的全局函數(shù)和變量,可以跨模塊的鏈接使用。
有一些名字定義所表示的實體擁有外部鏈接,這樣就意味著他可以跨越編譯單元去進行代碼的鏈接。所以,擁有外部鏈接的實體如果被聲明在頭文件并且被多個.cpp文件包含,可能就會出現(xiàn)鏈接沖突錯誤,因為每個包含這個擁有外部鏈接實體的.cpp都會分配空間,當(dāng)多個編譯單元鏈接的時候,連接器就會面對多個相同的名字,無法正常鏈接到正確的對象。
下面舉個例子:(VS2012環(huán)境下)
//lesson.h
namespace lesson
{
int test;
}
//lesson.cpp
#include "stdafx.h"
#include "lesson.h"
int _tmain(intargc,_TCHAR*argv[])
{
system("pause");
return0;
}
//test.cpp
#include "lesson.h"
我們就會看到
error LNK2005: "intlesson::test" (?test@lesson@@3HA) 已經(jīng)在 lesson.obj 中定義C:\Users\user\Documents\Visual Studio 2012\Projects\lesson\lesson\stdafx.obj
這樣的錯誤提示。
而對于擁有內(nèi)部鏈接的實體則不會出現(xiàn)這樣的情況,因為他不會與其他.cpp的同名實體產(chǎn)生沖突。比如我們將上面的lesson.h改為
//lesson.h
class lesson
{
int test;
}
這樣就不會有任何錯誤,因為類的定義是有內(nèi)部鏈接的。
如果在lesson.h里面再定義靜態(tài)變量,枚舉類,進行各種聲明等,這些實體由于有內(nèi)部鏈接所以仍然是合法的,編譯器會認(rèn)為你想在各個編譯單元中都有一個私有的副本。
那么進一步的概括這些內(nèi)容就是一句話
相同作用域內(nèi)的聲明可以有多個,但是只能定義一次。
先不考慮內(nèi)鏈接還是外鏈接,我們都知道一個{}里面不可能定義兩個一模一樣的名字。對于一個單獨的.cpp文件,我們是知道的,但是對于多個文件,好像就稍微有點暈。其實,這是一個道理,我們的外部鏈接就是讓各個.cpp文件能鏈接到一起,這樣在.cpp文件遇到第一個{}之前,他們的作用域就可以理解為相同的,所以擁有外部鏈接的實體(全局函數(shù),變量等)出現(xiàn)在第一個{}之前,而且名字相同,那就是出現(xiàn)了定義重復(fù)的錯誤。
我們再看,所有的聲明都是有內(nèi)部鏈接的,然而他其實可以鏈接到其他文件,因為他的定義是在其他的編譯單元的,所以多個編譯單元擁有相同的聲明也是合理的。但是,我們知道,這個聲明對應(yīng)的定義肯定只有一個。
最后再給出一個C++編程建議,慎重考慮在頭文件中定義有鏈接的實體
一,如果頭文件是像int a=1;這樣的定義,被包含在多個.cpp文件后肯定會報出鏈接錯誤。
二,如果是想static int a = 2;這樣的定義就會在所有包含他的.cpp文件中生成一個副本,如果被大量源文件include的話,就會占據(jù)大量的空間,造成內(nèi)存浪費。
總結(jié)
到此這篇關(guān)于C++內(nèi)鏈接與外鏈接意義的文章就介紹到這了,更多相關(guān)C++內(nèi)鏈接與外鏈接內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++關(guān)鍵字volatile學(xué)習(xí)筆記
這篇文章主要為大家介紹了C++關(guān)鍵字volatile學(xué)習(xí)筆記,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10
C++設(shè)計模式編程中簡單工廠與工廠方法模式的實例對比
這篇文章主要介紹了C++設(shè)計模式編程中簡單工廠與工廠方法模式的實例對比,文中最后對兩種模式的優(yōu)缺點總結(jié)也比較詳細(xì),需要的朋友可以參考下2016-03-03
如何基于 Blueprint 在游戲中創(chuàng)建實時音視頻功能
我們在本文先來講講如何在 Unreal 中用 Blueprint 快速實現(xiàn)。稍后會分享基于 C++的實現(xiàn)步驟。感興趣的朋友跟隨小編一起看看吧2020-05-05

