一、C++介紹
本賈尼 貝爾實驗室 在分析UNIX系統分布核心流量的時候,希望有一種有效的更加子產品化的工具
1979年10月完成了預處理器Cpre,為C增加了類機制,也就是面向對象。
1983年完成了C++的第一個版本,C with classes也就是C++
C++與C的不同點:
1、C++完全相容C的所有文法
2、支援面向對象的程式設計思想
3、支援運算符,函數重載
4、支援泛型程式設計,模版
5、支援異常處理
6、類型檢查嚴格
二、第一個C++程式
1、檔案擴充名
.cpp .cc .C .cxx
2、編譯器
g++ 大多數系統不帶的,需要額外安裝,ubuntu系統下的安裝指令:
sudo apt-get updata
sudo apt-get install g++
gcc也可以繼續使用但需要增加參數 -xC++ -lstdc++
3、頭檔案
#include
#include<stdio.h>可以繼續使用
#include也可以使用
4、輸入/輸出
count << 輸出資料
cin >> 輸入資料
以上兩個會自動識别類型
scanf/printf也可以繼續使用
注意:cout和cin是類對象,而scanf/printf是标準庫函數
5、增加了名字空間
std::count
using namespace std;
所有的标準類型,對象,函數都位于
三、名字空間
1、為什麼需要名字空間
在大型項目中,函數名、全局變量、結構、聯合、枚舉、類,非常有可能名字沖突,而名字空間就對這些命名進行邏輯空間劃分(不是實體單元劃分),為了解決命名沖突
2、什麼是名字空間
在C++中經常使用多個獨立開發的庫來完成項目,由于庫的作者或開發技術人員根本沒見過面,是以命名沖突在所難免,C++之父為防止命名沖突給C++設計一個名字空間機制
通過使用namespace XXX把庫中的變量,函數,類型,結構等包含在名字空間中,形成自己的作用域,避免名字沖突
namespace XXX
{
}//這個位置沒有分号
注意:名字空間也是一種辨別符,在同一作用域下不能重名
3、同名的名字空間會自動合并(為了申明和定義分開寫)
同名的名字空間中如果有重名的依然會命名沖突
4、名字空間的使用方法
::域限定符
空間名::表示符//使用麻煩 但非常安全
using namespace 空間名;把空間中定義的辨別符導入到目前代碼中
5、無名名字空間
不屬于任何名字空間的辨別符,隸屬于無名名字名字空間。
無名名字空間中的成員使用::辨別符 進行通路
如果通路被屏蔽的全局變量
6、名字空間的嵌套
名字空間内部可以在定義名字空間 這種名字叫做空間嵌套
内層名字空間與外層的名字空間的成員可以同名,内層會屏蔽外層的
多層名字空間在使用時逐層分解
n1::n2::n3::n4
namespace n1
{ int num = 1;
namespace n2
{
int num =2;
}
}
7、取别名
由于名字空間可以嵌套,這樣就會導緻在使用内層成員時過于麻煩,可以給名字空間取别名來解決這類問題
namespace n123 = n1::n2::n3;
四、C++的結構
1、不再需要 typedef,struct 關鍵字可以省略
2、成員可以是函數(成員函數),在成員函數中可以直接通路成員變量,不需要.或->,但是C的結構成員可以是函數指針
3、有一些隐藏的成員函數(構造,析構,拷貝構造,指派構造)
4、可以繼承,可以設定成員的通路權限(面向對象)
五、C++的聯合
1、不再需要 union ,在定義結構變量時,可以省略 union 關鍵字
2、成員可以是函數
3、有一些隐藏的成員函數
六、C++的枚舉
1、定義,使用方法與C語言基本一緻
2、類型檢查比C語言跟家嚴格
七、C++的布爾類型
1、C++中有真正的布爾類型,bool 是C++中的關鍵字,在C語言中使用布爾類型需要導入頭檔案stdbool.h(在c11中boll加入)
2、true false 在C++中是關鍵字,而在C語言中不是
3、C++中 true false 是1位元組,而在C語言中是4位元組
八、C++的void*
1、C語言中void* 可以于任意類型的指針
2、C++中void* 不能給其他類型的指針直接指派,必須強制類型轉換,但是其他類型的指針可以自動給void指派
3、C++為什麼這樣修改void
為了更安全,是以C++類型檢查更加嚴格
C++可以自動識别類型,對萬能指針的需求不再那麼強烈
九、操作符别名
某些特殊的語言,鍵盤上沒有~ &等符号,是以C++标準委員會為了讓C++更有競争力,為符号定義了一批别名,讓小語種也能寫C++
十、函數重載(重載、隐藏、重寫覆寫)
1、函數重載
在同一個作用域下,函數名相同,參數清單不同的函數,構成函數重載關系
2、重載實作的機制
C++代碼在編譯時會把函數的參數類型添加到參數名中,借助這個方式來實作函數重載,就是C++的函數在編譯期間經曆換名的過程
是以,C++代碼不能調用C語言編譯出的C函數(gcc)
3、extern “C”
告訴C++編譯器按照C語言的方式申明函數,這樣C++就可以調用C編譯器編譯出的函數了(C++目标檔案可以與C目标檔案合并生成可執行程式)
如果C想調用C++編譯出的函數,需要将C++函數的定義用extern"C"包括一下
注意:如果兩個函數名一樣,一定會沖突
4、重載和作用域
函數的重載關系發生在同一作用域下,不同的作用域下的同名關系叫做隐藏
5、重載解析
當調用函數時,編譯器根據實參的類型和形參的比對情況,選擇一個确定的重載版本,這個過程叫做重載解析
實參的類型和形參的比對情況有三種:
1、編譯器找到與實參最佳的比對函數,編譯器将生成調用代碼
2、編譯找不到比對函數,編譯器将給出錯誤資訊
3、編譯器找到好幾個比對函數,但是沒有一個是最佳的,這種錯誤叫做二義性。
在大多數情況下編譯器都能立即找到一個最佳的調用版本,但如果沒有,編譯就會類型提升,這樣被選函數中,可能會有多個可調用的版本,這樣就可能産生二義性錯誤。
6、重載确定的三個步驟
1、候選函數
函數調用的第一步就是确定所有可調用的函數的集合(函數名、作用域),該集合中的函數就是候選函數
2、選擇可行性函數
從候選函數中選擇一個或多個函數,選擇的标準是參數個數相同,而且通過類型提升實參可被隐式轉換為形參
3、尋找最佳比對
優先每個參數都完全比對的方案,其次參數完全比對的個數,在其次是浪費記憶體的位元組數。
7、指針類型也會對函數的重載造成影響
C++函數在編譯時,如果形參類型是指針,編譯時函數名中追加Px。
十一、預設形參
在C++中,函數的形參可以設定預設值,調用函數,如果沒有提供實參,則使用預設形參
如果形參隻有一部分設定了預設形參,則必須靠右排列
函數的預設形參是在編譯階段确定的,是以隻能使用常量,或者是常量表達式,或者全局變量做預設值。
如果函數的聲明和定義需要分開,隻需要在函數聲明時設定預設參數即可
設定預設形參時一定要慎重,預設形參對函數重載造成應影響
十二、内聯函數
1、普通函數調用時是生成調用指令(跳轉),然後當代碼執行到調用位置時跳轉到函數所在的代碼段中執行
2、内聯函數就是把函數編譯好的二進制指令直接複制到函數的調用位置
3、内聯函數的優點就是提高程式的運作速度(因為沒有跳轉,也不需要傳回),但這樣會導緻可執行檔案增大(備援),也就是犧牲空間來換取時間
4、内聯分為:顯式内聯和隐式内聯
顯式内聯:在函數前 inline(C語言C99标準也支援)
隐式内聯:結構、類中内部直接定義的成員函數,則該類型函數會被優化成内聯函數。
5、宏函數在調用時會把函數體直接替換到調用位置,與内聯函數一樣也是使用空間來換取時間
宏函數與内聯函數的差別(優缺點)
1、宏函數不是真正的函數,隻是代碼的替換,不會有參數壓棧,出棧以及傳回值,也不會檢查參數類型,是以所有的類型都可以使用,但這樣會有安全隐患。
2、内聯函數是真正的函數,函數調用時,會進行傳參、壓棧、出棧,可以有傳回值,并會嚴格檢查參數類型,但這樣就不能通用,如果想補多種類型調用需要重載。
6、内聯适用的條件
由于内聯會造成可執行檔案變大,并且增加記憶體開銷,是以隻有頻繁調用的簡單函數适合作為内聯函數。
調用比較少的複雜函數,内聯後并不顯著提高性能,不足以抵消犧牲空間帶來的損失,是以不适合内聯
帶有遞歸特性和動态綁定特性的函數,無法實施内聯,是以編譯器會忽略聲明部分的 inline 關鍵字
十三、引用
引用就是取藝名
1、引用的基本特性
引用就是取别名,申明一個辨別符為引用,就表示該辨別符是另一個對象的外号。
1、引用必須初始化,不存在空引用,但有懸空引用(變量死了,名還留着)
2、可以引用一個無名對象和臨時對象配合 const 使用
3、引用不能更換目标
引用一旦完成了定義和初始化就和普通變量名一樣了,他就代表了目标,一經引用終身不能在引用其他目标