C++自學34:原始檔與標頭檔案(pragma/ifndef/endif/ifdef)

很饞馬銀霜的身子發表於2020-12-23

本文所有操作都在vs2019下進行,在vs中“標頭檔案”和“原始檔”兩個資料夾是為了方便管理,假如把cpp檔案放到“標頭檔案”資料夾中,這也是沒有任何問題的

.cpp結尾的就是c++的原始檔
.c結尾的就是c語言的原始檔,在vs中如果想建立c的原始檔,只能選擇建立c++檔案,然後通過修改字尾名的方式,將其cpp改成c,點c的原始檔,只能執行c語言的程式碼,任何關於c++的語法和標頭檔案(比如iostream)都無法使用

用java對比的話,每一個cpp或者c檔案,都相當於java當中的一個類

下面有原始檔,分別是a.cpp和b.cpp
b.cpp原始碼如下

//b.cpp
#include <iostream>
void b1() {
	std::cout << "b.cpp->b1()" << std::endl;
}

a.cpp中呼叫b.cpp中的方法

void b1();//此處必須要先宣告才能使用
int main() {
	b1();
}

我們會發現,“宣告”這個操作特別的不友好,假設b.cpp中函式特別多,那麼a.cpp上面就要宣告很多函式,為了解決這個問題,我們使用標頭檔案來解決,在“標頭檔案”資料夾右鍵新建,選擇標頭檔案,通常標頭檔案名稱與原始檔名稱相同,本文新建b.h,內容如下

#pragma once//這個關鍵字下文會有說明
void b1();

有了標頭檔案之後,a.cpp的程式碼改成下面這樣,原因是因為#include是將b.h檔案中的內容複製過來,和#define差不多

#include "b.h";//注意此處是雙引號,而不是尖括號
int main() {
	b1();
}

標頭檔案中不僅可以宣告,也可以定義,但是通常不這麼做,因為很可能會產生衝入,比如當a.cpp當中也有b1這個方法的時候,切記:在同一個工程中,宣告可以多次,定義只有一次

站在編譯器的角度:編譯器不會編譯標頭檔案,只有在cpp檔案中使用h檔案的時候,編譯器才將這個h檔案的內容複製過來,假設有一個h檔案,裡面隨便寫任何東西,但是沒有cpp檔案使用它,此時編譯也不會報錯,由此可以證明編譯器不會編譯這個h檔案

在h檔案中定義全域性變數

//b.cpp檔案中程式碼如下
#pragma once
void b1();
extern int mys;//和上邊的程式碼相比,多出了這行程式碼

新建一個c.cpp檔案

//c.cpp檔案
#include "b.h"
void c1() {
	mys++;
}

在a.cpp的main函式下執行下面程式碼

#include <iostream>
#include "b.h"
void c1();
//注意此處,b.cpp中的extern是宣告,必須還要有一個定義才行,所以在此處對
//mys進行定義,這樣,a.cpp和c.cpp就能共用一個mys變數了
int mys = 0;
int main() {
	mys++;
	c1();
	std::cout << mys << std::endl;
}

#pragma once:表示在其他的同一個檔案中,只引用我這個檔案一次
解決迴圈依賴問題,假如a.h檔案引用了b.h檔案,而b.h又引用了a.h,那麼根據預處理命令的語義,就會出現a一直迴圈引用b,b一直迴圈引用a的情況,此時需要#pragma once來解決

//b.h
#pragma once
void b1();

由於#pragma once的存在,下面的a.cpp寫多少次include都沒有關係

//a.cpp
#include "b.h"
#include "b.h"//多次include沒有關係,因為b.h中寫了pragma once
#include "b.h"
#include "b.h"
#include "b.h"
int main() {
	
}

注意:有些編譯器不支援pragma,此時使用#ifndef和#endif來解決,不過我覺得寫筆記太麻煩了,就沒有在筆記中記錄,但是ifndef相對pragma比,有一個優勢,就是它可以定義在任意程式碼塊,而pragma是整個檔案,還有一個叫#ifdef,少個n,不過通常ifndef和ifdef用於版本控制,比如一個遊戲,有vip版本和普通版本


18.3

相關文章