關鍵字

?命名空間
命名空間解決了C沒辦法解決的各類命名沖突問題
C++的標準命名空間:std
命名空間中可以定義變量、函數、類型:
namespace CS {//變量char cs408[] = "DS,OS,JW,JZ";int cs = 408;//函數void Func() {cout << "085400" << endl;}//類class student {public:void F() {cout << "bull and horse" << endl;}private:int a = 6;int b = 66;};
}
且命名空間可以嵌套(不常用):
namespace School {int x = 0;namespace CS {//};};
授權
命名空間未展開時,默認不能訪問命名空間
全部展開:
using namespace CS;
部分展開(僅可訪問部分已展開的變量、函數、類):
using CS::cs408;
或者加上作用域限定符直接訪問:
CS::student st1;st1.F();
合并
如果定義了多個同名的命名空間,會自動合并:
如果合并后有多個同名變量、函數、類型,會出現重定義。
缺省參數
聲明或者定義函數時,為參數指定的缺省值,如沒有傳參時,使用缺省值:
Date(int year = 1, int month = 1, int day = 1) {};
全缺省:
Date(int year = 1, int month = 1, int day = 1) {};
半缺省(部分缺省):
半缺省參數必須從右往左給出,且不能間隔著給。
所以,調用半缺省參數的函數時,傳參必須從左往右給出,同樣不能間隔。
Date(int year, int month = 1, int day = 1) {};Date(2025);
Date(int year, int month, int day = 1) {};Date(2025,5);
注:在函數聲明和定義分離的情況下,不能為聲明和定義同時給出半缺省參數,避免聲明和定義中的缺省值不同而無法調用。
函數重載
C++允許同一作用域下存在同名函數,條件是:形參的個數 or 類型 or 順序不同:
Date(int a);
Date(int a, int b);
Date(int a, char b);
Date(char a, int b);
原理
C/C++ 中,一個程序要運行需經歷:預處理、編譯、匯編、鏈接;
(每日回顧:C程序預處理(本文包括宏定義)-CSDN博客)
在匯編階段,對于一個函數,匯編器會為其分配一個地址,并將該地址與函數名關聯起來,記錄在符號表中;在鏈接完成之后,會形成完整的符號表。符號表包含了程序中所有符號的名稱、地址和屬性等信息。

?由上圖可見,C語言僅在函數名前加 '_' 以在符號表中查找對應函數地址;而C++的函數名修飾中,還與形參有關,這也印證了前文所說函數重載的條件。
特殊情況
函數重載和缺省參數的函數同時存在可能會出現調用歧義:
//無參數 和 全缺省
void F() {};
void F(int a = 1, int b = 2) {};//一個參數 和 半缺省
void F(int a) {};
void F(int a, int b = 2) {};
引用
引用的底層和C指針一樣,只是使用更加方便;一個變量的引用,就是給這個已存在的變量取個別名,這個引用與已存在的變量共用同一塊內存空間。
int p = 0;
int& pp = p; // pp是p的引用pp = 1; // 修改pp,p也會隨之修改
特性
1、必須在定義時就初始化;
2、一個變量可以有多個引用:
int p = 0;
int& pp = p;
int& ppp = p;
3、引用被初始化之后,不能再改變指向;
int p = 0;
int q = 1;
int& pp = p;
pp = q; // 報錯,不能再改變指向
使用場景
引用作為參數
void test(int& a){};
int main(){int b = 1;test(b);return 0;
}
引用作為返回值
int& test(int& a){a++;return a;
};
int main(){int b = 1;int ret = test(b);return 0;
}
?上面這段代碼,其實test函數中返回的a變量,在出了函數作用域之后已經銷毀,只不過我們用ret立刻接收了該引用的值;實際上返回的引用,已經是被釋放的空間。
所以,如果函數返回時,出了函數作用域,如果返回對象還在(還沒還給系統),則可以使用引用返回,如果已經還給系統了,則必須使用傳值返回。
效率
使用傳值傳參和使用值作為返回值類型時,實際上都是實參或者返回變量的一份臨時拷貝;所以當值較大時,效率必然低于使用引用傳參或者使用引用作為返回值類型。
和指針對比
引用的底層實現,與指針相同,只不過使用時更為方便。
1、引用 在概念上是一個變量的別名,而指針存儲地址;
2、引用在定義時必須初始化指向實體,指針不必;
3、引用在初始化引用一個實體后,不能再改變指向,而指針可以;
4、sizeof(引用) 是引用類型的大小,而指針始終是地址所占字節數大小;
5、引用++ 是引用的實體++,而指針++ 是向后偏移;
6、沒有多級引用,有多級指針;
7、訪問實體方式不同:引用 編譯器會處理,而指針需要顯式解引用;
8、引用使用起來更安全。
內聯函數
以inline修飾的函數為內聯函數。
普通函數在調用時,需要建立棧幀;內聯函數會在編譯時直接展開,沒有建立棧幀的開銷,提升程序運行效率。
有點像C中的宏函數,只不過宏是完全的只有替換,而內聯函數是展開函數體。
inline void test(int a){};
特性
1、以空間換時間,但如果函數體過大,會使文件變大;
2、不同編譯器的實現不同,一般編譯器會將函數規模較小(10行左右)、且不是遞歸、且頻繁調用的函數,采用inline修飾,否則會忽略。(內聯說明只是向編譯器發出的一個請求,編譯器可以選擇忽略這個請求)
3、inline函數不建議聲明和定義分離。因為內聯函數展開后,就沒有函數地址了,只有聲明就會找不到定義,報鏈接錯誤。
auto關鍵字(C++11)
auto作為一 個新的類型指示符來指示編譯器,auto聲明的變量必須由編譯器在編譯時期推導而得。簡單來說,auto可以推導當前變量類型。
int a = 0;
auto b = a;
auto c = 'a';
使用auto定義變量時必須對其進行初始化,在編譯階段編譯器需要根據初始化表達式來推導auto 的實際類型。因此auto并非是一種“類型”的聲明,而是一個類型聲明時的“占位符”,編譯器在編譯期會將auto替換為變量實際的類型。
你不能使用auto來推導未知類型。
auto常用來與范圍for、lambda表達式搭配使用。
范圍for(C++11)
范圍for使得在遍歷一個有范圍的集合時,更加方便:
int arr[10] = {0};
for (auto a : arr){cout << a << endl;
}
這段代碼會依次取數組中所有的數據賦值給 a ,打印;
使用條件
1、for 循環迭代的范圍是確定的(下圖展示了不確定的范圍);
void test(int arr[]){for(auto a : arr){cout << a << endl;}
}
2、迭代的對象要實現++和==的操作。
nullptr
int* p1 = NULL;
int* p2 = 0;
在C的頭文件 stddef.h 中,有部分代碼:
(條件編譯見每日回顧:C程序預處理(本文包括宏定義)-CSDN博客)
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
NULL實際是一個宏,可能被定義為字面常量0,或者被定義為無類型指針(void*)的常量。
但是如果有:
void test(int a)
void test(int* a){};
int main(){test(NULL);
}
那么test(NULL) 到底調用誰?這就出現了問題,C++11中通過引入關鍵字nullptr 用來初始化空指針。