文章目錄
- 什么是C++
- C++的發展史
- C++關鍵字
- 命名空間
- 命名空間的定義
- 1.命名空間的普通定義
- 2.命名空間可以嵌套
- 3. 同一個工程中允許存在多個相同名稱的命名空間,編譯器最后會合成同一個命名空間中,
- 命名空間使用
- 1.加命名空間名稱及作用域限定符
- 2.使用using namespace 命名空間名稱引入
- 3.使用using將命名空間中的成員引入
- C++中的輸入和輸出
- 預設引數
- 全預設
- 半預設引數
- 函式多載
- 函式多載的原理
- extern “C”
- 參考
- 參考的特征
- 1.參考在定義時必須初始化
- 2.一個變數可以有多個參考
- 3.參考一旦參考了一個物體,就不能再參考其他物體
- 常參考
- 參考的使用場景
- 1.參考做引數
- 2.參考做回傳值
- 參考和指標的區別
- 行內函式
- 特性
- c++有哪些技術可以代替宏
- auto關鍵字(C++11)
- auto的使用細則
- 1.auto與指標和參考結合起來使用
- 2.在同一行定義多個變數
- auto不能推導的場景
- 1.auto做為函式的引數
- 2.auto不能直接用來宣告陣列
- 基于范圍的for回圈(C++11)
- 范圍for的語法
- 范圍for的使用條件
- 1.for回圈迭代的范圍必須是確定的
- 2. 迭代的物件要實作++和==的操作,
- 指標空值nullptr
- C++98中的指標空值
- C++11中的指標空值
什么是C++
C語言是結構化和模塊化的語言,適合處理較小規模的程式,對于復雜的問題,規模較大的程式,需要高度
的抽象和建模時,C語言則不合適,為了解決軟體危機, 20世紀80年代, 計算機界提出了OOP(object
oriented programming:面向物件)思想,支持面向物件的程式設計語言應運而生,
1982年,Bjarne Stroustrup博士在C語言的基礎上引入并擴充了面向物件的概念,發明了一種新的程式語
言,為了表達該語言與C語言的淵源關系,命名為C++,因此:C++是基于C語言而產生的,它既可以進行C語
言的程序化程式設計,又可以進行以抽象資料型別為特點的基于物件的程式設計,還可以進行面向物件的程
序設計,
C++的發展史
1979年,貝爾實驗室的本賈尼等人試圖分析unix內核的時候,試圖將內核模塊化于是在C語言的基礎上進行擴展,增加了類的機制,完成了一個可以運行的預處理程式,稱之為C with classes,
語言的發展也是隨著時代的進步,在逐步遞進的,讓我們來看看C++的歷史版本:
| 階段 | 內容 |
|---|---|
| C with classes | 類及派生類、公有和私有成員、類的構造析構、友元、行內函式、賦值運算子多載等 |
| C++1.0 | 添加虛函式概念,函式和運算子多載,參考、常量等 |
| C++2.0 | 更加完善支持面向物件,新增保護成員、多重繼承、物件的初始化、抽象類、靜態成員以及const成員函式 |
| C++3.0 | 進一步完善,引入模板,解決多重繼承產生的二義性問題和相應構造和析構的處理 |
| C++98 | C++標準第一個版本,絕大多數編譯器都支持,得到了國際標準化組織(ISO)和美國標準化協會認可,以模板方式重寫C++標準庫,引入了STL(標準模板庫) |
| C++03 | C++標準第二個版本,語言特性無大改變,主要∶修訂錯誤、減少多異性 |
| C++05 | C++標準委員會發布了一份計數報告(Technical Report,TR1),正式更名C++0x,即∶計劃在本世紀第一個10年的某個時間發布 |
| C++11 | 增加了許多特性,使得C++更像一種新語言,比如∶正則運算式、基于范圍for回圈、auto關鍵字、新容器、串列初始化、標準執行緒庫等 |
| C++14 | 對C++11的擴展,主要是修復C++11中漏洞以及改進,比如∶泛型的lambda運算式,auto的回傳值型別推導,二進制字面常量等 |
| C++17 | 在C++11上做了一些小幅改進,增加了19個新特性,比如∶static_assert()的文本資訊可選,Fold運算式用于可變的模板,if和switch陳述句中的初始化器等 |
C++關鍵字
C++中總計有63個關鍵字:
其中畫圈的是C語言的關鍵字,
這里要注意了:false和true并不是C語言的關鍵字,
命名空間
在C/C++中,變數、函式和類都是大量存在的,這些變數、函式和類的名稱都將作用于全域作用域中,可能會導致很多命名沖突,
使用命名空間的目的就是對識別符號和名稱進行本地化,以避免命名沖突或名字污染,namespace關鍵字的出現就是針對這種問題的,
命名空間的定義
定義命名空間,需要使用到namespace關鍵字,后面跟命名空間的名字,然后接一對{}即可,{}中即為命名
空間的成員,
注意:一個命名空間就定義了一個新的作用域,命名空間中的所有內容都局限于該命名空間中
1.命名空間的普通定義
//1. 普通的命名空間,里面可以定義變數,也可以定義函式
namespace xjt
{
int printf = 1;
int rand = 2;
int Add(int a, int b)
{
return a + b;
}
}
2.命名空間可以嵌套
//2.命名空間可以嵌套
namespace xjt
{
int printf = 1;
int rand = 2;
int Add(int a, int b)
{
return a + b;
}
namespace xjt2
{
int a = 0;
int Sub(int a, int b)
{
return a - b;
}
}
}
3. 同一個工程中允許存在多個相同名稱的命名空間,編譯器最后會合成同一個命名空間中,
//3. 同一個工程中允許存在多個相同名稱的命名空間,編譯器最后會合成同一個命名空間中,
namespace xjt
{
int a = 3;
int b = 1;
}
它會與上面的xjt命名空間合并
命名空間使用
下面來看這么一段代碼
namespace xjt
{
int printf = 1;
int rand = 2;
int Add(int a, int b)
{
return a + b;
}
}
#include<iostream>
int main()
{
printf("%d\n",printf); //這樣列印出來的結果和我們預期的不一樣,因為你這樣呼叫的是printf的地址通過下面兩個可以加深理解
printf("%p\n", printf); //6A35CE70
printf("%p\n", rand); //6A42FAB0;
}

很顯然直接列印printf是不可能的,因為你這樣呼叫的是printf的地址,所以會出現的這樣的結果,正面的呼叫方法為以下三種,
1.加命名空間名稱及作用域限定符
符號“::”在C++中叫做作用域限定符,我們通過“命名空間名稱::命名空間成員”便可以訪問到命名空間中相應的成員

2.使用using namespace 命名空間名稱引入

但是這種方式存在著一些弊端,如果我們在命名空間中定義了一個名字為printf的變數,那么之后再將namespace xjt這個命名空間引入的話,就會造成命名的污染了,
為了解決這個問題,出現了第三種引入方法,
3.使用using將命名空間中的成員引入

這種方法可以防止命名的污染,因為它只引入了一部分,
C++中的輸入和輸出
新生嬰兒會以自己獨特的方式向這個嶄新的世界打招呼,C++剛出來后,也算是一個新事物,那C++是否也應該向這個美好的世界來聲問候呢?我們來看下C++是如何來實作問候的,
#include<iostream>
using namespace std;
int main()
{
cout<<"Hello world!!!"<<endl;
return 0;
}
在C語言中有標準輸入輸出函式scanf和printf,而在C++中有cin標準輸入和cout標準輸出,在C語言中使用scanf和printf函式,需要包含頭檔案stdio.h,在C++中使用cin和cout,需要包含頭檔案iostream以及std標準命名空間,
C++的輸入輸出方式與C語言更加方便,因為C++的輸入輸出不需要控制格式,例如:整型為%d,字符型為%c,
#include<iostream>
using namespace std;
int main()
{
int a = 1;
float b = 2.1;
double c= 2.111;
char arr[10] = { 0 };
char d[] = "hello world";
cin >> arr;
cout << arr << endl;
cout << a << endl;
cout << b << endl;
cout << c << endl;
cout << d << endl;
return 0;
}
注意:endl,這其中的l不是阿拉伯數字1,而是26個英文字母的l,它的作用相當于換行,
這里我們還要注意下cin的特點,他和C語言中的gets有些像,gets是遇到換行符停止,而cin是以遇到空格,tab或者換行符作為分隔符的,因此這兒輸入hello world會被空格符分隔開來,
這兒我輸入的是hello world,但因為輸入時出現了空格,所以之后的內容并不會讀入,因此arr中存的就是hello,
預設引數
預設引數是宣告或定義函式時為函式的引數指定一個默認值,在呼叫該函式時,如果沒有指定實參則采用該
默認值,否則使用指定的實參,
//預設引數
#include<iostream>
using namespace std;
//這兒的0就相當于預設引數,如果實參什么都沒傳過來,預設引數就賦值給a,相當于備胎的意思,
void func(int a = 0)
{
cout << a << endl;
}
int main()
{
func(10);
func(); //在c語言中這樣寫肯定是不行的,但是在c++中有了預設引數,如果你什么都不傳,只要你前面有預設引數的存在,就能過,
return 0;
}

全預設
全預設引數,即函式的全部形參都設定為預設引數,
//全預設
#include<iostream>
using namespace std;
void func(int a = 0, int b = 1, int c = 2)
{
cout <<"a="<< a << endl;
cout << b << endl;
cout << c << endl;
}
int main()
{
func();
return 0;
}
半預設引數
void func(int a, int b, int c = 2)
{
cout << a << endl;
cout << b << endl;
cout << c << endl;
}
注意:?
1、半預設引數必須從右往左依次給出,不能間隔著給,
//錯誤示例
void func(int a, int b = 2, int c)
{
cout << a << endl;
cout << b << endl;
cout << c << endl;
}
2、預設引數不能在函式宣告和定義中同時出現
//錯誤示例
//test.h
void func(int a, int b, int c = 3);
//test.c
void func(int a, int b, int c = 2)
{
cout << a << endl;
cout << b << endl;
cout << c << endl;
}
因為:如果宣告與定義位置同時出現,恰巧兩個位置提供的值不同,那編譯器就無法確定到底該用那
個預設值,
3、預設值必須是常量或者全域變數,
//正確示例
int x = 3;//全域變數
void func(int a, int b = 2, int c = x)
{
cout << a << endl;
cout << b << endl;
cout << c << endl;
}
函式多載
函式多載:是函式的一種特殊情況,C++允許在同一作用域中宣告幾個功能類似的同名函式,這些同名函式的
形參串列(引數個數 或 型別 或 順序)必須不同,常用來處理實作功能類似資料型別不同的問題
#include <iostream>
using namespace std;
int Add(int x, int y)
{
return x + y;
}
double Add(double x, double y)
{
return x + y;
}
int main()
{
cout << Add(0,1) << endl;//列印0+1的結果
cout << Add(1.1,2.2) << endl;//列印1.1+2.2的結果
return 0;
}
注意:若僅僅只有回傳值不同,其他都相同,則不構成函式多載,
short Add(short left, short right)
{
return left+right;
}
int Add(short left, short right)
{
return left+right;
}
函式多載的原理
為什么C++支援函式多載,而C語言不可以了?
這里我們就要回顧一下以前的知識了,在運行到執行檔案前,要經過:預編譯,編譯,匯編,鏈接這些階段
其實問題就出在編譯完之后的匯編階段,因為在這里C++和C語言有著些許的不同,下面我們來看看:
采用C語言編譯器編譯之后

采用C++編譯器編譯之后

總結:
1.其實歸根到底,還是因為C編譯器和C++編譯器對函式名的修飾不同,在gcc下的修飾規則是:【_Z+函式長度+函式名+類
型首字母】,
2.這其實也告訴我們為什么函式的回傳型別不同,不會構成函式多載,因為修飾規則并不會受回傳值的影響,
extern “C”
有時候在C++工程中可能需要將某些函式按照C的風格來編譯,在函式前加extern “C”,意思是告訴編譯器,
將該函式按照C語言規則來編譯,比如:tcmalloc是google用C++實作的一個專案,他提供tcmallc()和tcfree
兩個介面來使用,但如果是C專案就沒辦法使用,那么他就使用extern “C”來解決,
參考
參考不是新定義一個變數,而是給已存在變數取了一個別名,編譯器不會為參考變數開辟記憶體空間,它和它
參考的變數共用同一塊記憶體空間,型別& 參考變數名(物件名) = 參考物體;
#include<iostream>
using namespace std;
int main()
{
int a = 1;
int&b = a; //相當于給a起了一個別名為b,int是b的型別
cout << a << endl;
cout << b << endl;
b = 3; //改變b也就相當于改變了a
cout << b << endl;
cout << a << endl;
}

注意:參考型別必須和參考物體是同種型別的
參考的特征
1.參考在定義時必須初始化
//正確示例
int a = 10;
int& b = a;//參考在定義時必須初始化
//錯誤示例
int a = 10;
int &b;//定義時未初始化
b = a;
2.一個變數可以有多個參考
int a = 10;
int& b = a;
int& c = a;
int& d = a;
3.參考一旦參考了一個物體,就不能再參考其他物體
int a = 10;
int& b = a;
int c = 20;
b = c;//你的想法:讓b轉而參考c

但實際的效果,確實將c的值賦值給b,又因為b是a的參考,所以a的值見解變成了20,
常參考
上面提到,參考型別必須和參考物體是同種型別的,但是僅僅是同種型別,還不能保證能夠參考成功,這兒我們還要注意可否可以修改的問題,
void TestConstRef()
{
const int a = 10;
//int& ra = a; // 該陳述句編譯時會出錯,a為常量
const int& ra = a;
// int& b = 10; // 該陳述句編譯時會出錯,b為常量
const int& b = 10;
double d = 12.34;
//int& rd = d; // 該陳述句編譯時會出錯,型別不同
const int& rd = d;
}
這里的a,b,d都是常量,常量是不可以被修改的,但是如果你用int&ra等這樣來參考a的話,那么參考的這個a是可以被修改的,因此會出問題,
下面我們來看這么一段代碼:
#include<iostream>
using namespace std;
int main()
{
int a = 10;
double&ra = a;
}
這個參考對嗎?想要弄明白這個問題,首先要明白隱士型別提升的問題,在這里int到double存在隱士型別的提升,而在提升的程序中系統會創建一個常量區來存放a型別提升后的結果,因此到這兒,這段代碼一看就是錯了,因為你隱士型別提升時a是存放在常量區中的,常量區是不可以被修改的,而你用double&ra去參考他,ra這個參考是可以被修改的,
加個const就可以解決這個問題,
#include<iostream>
using namespace std;
int main()
{
int a = 10;
const double&ra = a;
}
注意:將不可修改的量用可讀可寫的量來參考是不可以的,但是反過來是可以的,將可讀可寫的量用只可讀的量來參考是可以的,
參考的使用場景
1.參考做引數
還記得C語言中的交換函式,學習C語言的時候經常用交換函式來說明傳值和傳址的區別,現在我們學習了參考,可以不用指標作為形參了,因為在這里a和b是傳入實參的參考,我們將a和b的值交換,就相當于將傳入的兩個實參交換了,
//交換函式
void Swap(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
}
2.參考做回傳值
當然參考也能做回傳值,但是要特別注意,我們回傳的資料不能是函式內部創建的普通區域變數,因為在函式內部定義的普通的區域變數會隨著函式呼叫的結束而被銷毀,我們回傳的資料必須是被static修飾或者是動態開辟的或者是全域變數等不會隨著函式呼叫的結束而被銷毀的資料,
不加static的后果

你是不是疑惑為什么列印的不是2而是7了?

這人就更奇怪了,為什么中間加了一句printf,就列印隨機值了?
下面我們來看看分析:

為什么會出現隨機值,因為你在函式里定義的變數是臨時變數,出了函式函式是會銷毀的,這時它就隨機指向記憶體中的一塊空間了,所以在參考做函式回傳值時最好還是給在函式中定義的變數加上static,
這時你覺得你真的懂這段代碼了嗎?
#include<iostream>
using namespace std;
int& Add(int a, int b)
{
static int c = a + b;
return c;
}
int main()
{
int& ans = Add(1,2);
Add(3, 4);
cout << ans << endl;
}

可能你會好奇了?為什么這兒是3了?下面來看看分析

其實你換種寫法,這兒的結果就會換成7,原因也很簡單,正是上面圖片中說的原因

注意:如果函式回傳時,出了函式作用域,回傳物件還未還給系統,則可以使用參考回傳;如果已經還給系統了,則必須使用傳值回傳,
這句話說的是下面這種例子:
int& Add(int a, int b)
{
int c=a+b; //出了函式作用域,c不在,回給了系統
return c;
}
int& Add(int a,int b)
{
static c=a+b; //出了函式作用域,c還在,可以用參考回傳
return c;
}
大家是不是感覺這個傳參考回傳用起來很怪了,下面我們來分析一下它是如何回傳的,

總結:
傳值的程序中會產生一個拷貝,而傳參考的程序中不會,其實在做函式引數時也具有這個特點,
參考和指標的區別
在語法概念上參考就是一個別名,沒有獨立空間,和其參考物體共用同一塊空間,
int main()
{
int a = 10;
int& ra = a;
cout<<"&a = "<<&a<<endl;
cout<<"&ra = "<<&ra<<endl;
return 0; }

在底層實作上實際是有空間的,因為參考是按照指標方式來實作的,
int main()
{
int a = 10;
int& ra = a;
ra = 20;
int* pa = &a;
*pa = 20;
return 0; }
我們來看下參考和指標的匯編代碼對比

參考和指標的區別
1、參考在定義時必須初始化,指標沒有要求,
2、參考在初始化時參考一個物體后,就不能再參考其他物體,而指標可以在任何時候指向任何一個同型別物體,
3、沒有NULL參考,但有NULL指標,
4、在sizeof中的含義不同:參考的結果為參考型別的大小,但指標始終是地址空間所占位元組個數(32位平臺下占4個位元組),
5、參考進行自增操作就相當于物體增加1,而指標進行自增操作是指標向后偏移一個型別的大小,
6、有多級指標,但是沒有多級參考,
7、訪問物體的方式不同,指標需要顯示解參考,而參考是編譯器自己處理,
8、參考比指標使用起來相對更安全,
行內函式
概念:以inline修飾的函式叫做行內函式,編譯時C++編譯器會在呼叫行內函式的地方展開,沒有函式壓堆疊的開銷,
行內函式提升程式運行的效率,(看到在加粗部分時,小伙伴肯定會想,這和c語言中的宏是不是很像了?)

如果在上述函式前增加inline關鍵字將其改成行內函式,在編譯期間編譯器會用函式體替換函式的呼叫

特性
- inline是一種以空間換時間的做法,省去呼叫函式額開銷,所以代碼很長/遞回的函式不適宜
使用作為行內函式,- inline對于編譯器而言只是一個建議,編譯器會自動優化,如果定義為inline的函式體內代碼比較長/遞回等
等,編譯器優化時會忽略掉行內,- inline不建議宣告和定義分離,分離會導致鏈接錯誤,因為inline被展開,就沒有函式地址了,鏈接就會
找不到,
//F.h
#include <iostream>
using namespace std;
inline void f(int i);
// F.cpp
#include "F.h"
void f(int i) {
cout << i << endl;
}
// main.cpp
#include "F.h"
int main()
{
f(10);
return 0;
}
// 鏈接錯誤:main.obj : error LNK2019: 無法決議的外部符號 "void __cdecl f(int)" (?
// f@@YAXH@Z),該符號在函式 _main 中被參考
c++有哪些技術可以代替宏
C++有哪些技術替代宏?
- 常量定義 換用const
- 函式定義 換用行內函式
auto關鍵字(C++11)
在早期的C/C++中auto的含義是:使用auto修飾的變數是具有自動存盤器的區域變數,但遺憾的是一直沒有人去使用它,
在C++11中,標準委員會賦予了auto全新的含義:auto不再是一個存盤型別指示符,而是作為一個新的型別指示符來指示編譯器,auto宣告的變數必須由編譯器在編譯時期推導而得, 可能光看這一句話,你不一定能懂,下面我們舉幾個例子,
#include<iostream>
using namespace std;
int TestAuto()
{
return 10;
}
int main()
{
int a = 10;
auto b = a;
auto c = 'a';
auto d = TestAuto();
cout << typeid(b).name() << endl; //這個地方要學到后面類的時候才可以解釋,這里列印出的是型別名
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;
cout << a << endl;
cout << b<< endl;
cout << c << endl;
cout << d << endl;
//auto e; 無法通過編譯,使用auto定義變數時必須對其進行初始化
return 0;
}

注意:使用auto定義變數時必須對其進行初始化,在編譯階段編譯器需要根據初始化運算式來推導auto的實際類
型,因此auto并非是一種“型別”的宣告,而是一個型別宣告時的“占位符”,編譯器在編譯期會將auto替換為
變數實際的型別,
auto的使用細則
1.auto與指標和參考結合起來使用
用auto宣告指標型別時,用auto和auto*沒有任何區別,但用auto宣告參考型別時則必須加&
#include <iostream>
using namespace std;
int main()
{
int a = 10;
auto b = &a; //自動推匯出b的型別為int*
auto* c = &a; //自動推匯出c的型別為int*
auto& d = a; //自動推匯出d的型別為int
//列印變數b,c,d的型別
cout << typeid(b).name() << endl;//列印結果為int*
cout << typeid(c).name() << endl;//列印結果為int*
cout << typeid(d).name() << endl;//列印結果為int
return 0;
}
注意:用auto宣告參考時必須加&,否則創建的只是與物體型別相同的普通變數,只不過將其換了個姓名而已,
2.在同一行定義多個變數
當在同一行宣告多個變數時,這些變數必須是相同的型別,否則編譯器將會報錯,因為編譯器實際只對
第一個型別進行推導,然后用推匯出來的型別定義其他變數,
void TestAuto()
{
auto a = 1, b = 2;
auto c = 3, d = 4.0; // 該行代碼會編譯失敗,因為c和d的初始化運算式型別不同
}
auto不能推導的場景
1.auto做為函式的引數
// 此處代碼編譯失敗,auto不能作為形參型別,因為編譯器無法對a的實際型別進行推導
void TestAuto(auto a)
{}
2.auto不能直接用來宣告陣列
void TestAuto()
{
int a[] = {1,2,3};
auto b[] = {4,5,6};
}
為了避免與C++98中的auto發生混淆,C++11只保留了auto作為型別指示符的用法
auto在實際中最常見的優勢用法就是跟以后會講到的C++11提供的新式for回圈,還有lambda運算式等
進行配合使用,
基于范圍的for回圈(C++11)
范圍for的語法
在C++98中如果要遍歷一個陣列,可以按照以下方式進行:
void TestFor()
{
int array[] = { 1, 2, 3, 4, 5 };
//將陣列所有元素乘以2
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
array[i] *= 2;
for (int* p = array; p < array + sizeof(array)/ sizeof(array[0]); ++p)
cout << *p << endl; }
對于一個有范圍的集合而言,由程式員來說明回圈的范圍是多余的,有時候還會容易犯錯誤,因此C++11中
引入了基于范圍的for回圈,for回圈后的括號由冒號“ :”分為兩部分:第一部分是范圍內用于迭代的變數,
第二部分則表示被迭代的范圍,
注意不能寫成auto,不然改變不了原陣列

正確的寫法
void TestFor()
{
int array[] = { 1, 2, 3, 4, 5 };
//將陣列中所有元素乘以2
for(auto& e : array)
e *= 2;
for(auto e : array)
cout << e << " ";
return 0;
}
注意:與普通回圈類似,可用continue來結束本次回圈,也可以用break來跳出整個回圈,
范圍for的使用條件
1.for回圈迭代的范圍必須是確定的
對于陣列而言,就是陣列中第一個元素和最后一個元素的范圍;對于類而言,應該提供begin和end的
方法,begin和end就是for回圈迭代的范圍,
注意:以下代碼就有問題,因為for的范圍不確定
void TestFor(int array[])
{
for(auto& e : array) //這里的array其實不是陣列,陣列在傳參時會退化成指標
cout<< e <<endl; }
2. 迭代的物件要實作++和==的操作,
關于迭代器這個問題,以后會講,現在大家了解一下就可以了,
指標空值nullptr
C++98中的指標空值
在良好的C/C++編程習慣中,在宣告一個變數的同時最好給該變數一個合適的初始值,否則可能會出現不可預料的錯誤,比如未初始化的指標,如果一個指標沒有合法的指向,我們基本都是按如下方式對其進行初始化:
int* p1 = NULL;
int* p2 = 0;
NULL其實是一個宏,在傳統的C頭檔案(stddef.h)中可以看到如下代碼:
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
可以看到,NULL可能被定義為字面常量0,或者被定義為無型別指標(void*)的常量,不論采取何種定義,在
使用空值的指標時,都不可避免的會遇到一些麻煩,比如:
#include <iostream>
using namespace std;
void Fun(int p)
{
cout << "Fun(int)" << endl;
}
void Fun(int* p)
{
cout << "Fun(int*)" << endl;
}
int main()
{
Fun(0); //列印結果為 Fun(int)
Fun(NULL); //列印結果為 Fun(int)
Fun((int*)NULL); //列印結果為 Fun(int*)
return 0;
}
程式本意本意是想通過Fun(NULL)呼叫指標版本的Fun(int* p)函式,但是由于NULL被定義為0,Fun(NULL)最終呼叫的是Fun(int p)函式,
注:在C++98中字面常量0,既可以是一個整型數字,也可以是無型別的指標(void*)常量,但編譯器默認情況下將其看成是一個整型常量,如果要將其按照指標方式來使用,必須對其進行強制轉換,
C++11中的指標空值
對于C++98中的問題,C++11引入了關鍵字nullptr,
在使用nullptr表示指標空值時,不需要包含頭檔案,因為nullptr是C++11作為關鍵字引入的,
在C++11中,sizeof(nullptr)與sizeof((void*)0)所占的位元組數相同,大小都為4,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/302553.html
標籤:其他


