一、本文主要內容
1、基本的內置類型
2、變量
3、複合類型
4、const限定符詳解
5、處理類型
二、基本內置類型
1、主要分爲算數類型和空類型
空類型:不對應具體的值,僅使用在一些特殊的場合
類型 | 含義 | 最小尺寸 |
---|---|---|
bool | 布爾類型 | 未定義 |
char | 字符 | 8位 |
wchar_t | 寬字符 | 16位 |
char16_t | Unicode 字符 | 16位 |
char32_t | Unicode 字符 | 32位 |
short | 短整型 | 16位 |
int | 整型 | 16位 |
long | 長整型 | 32位 |
long long | 長整型 | 64位 |
float | 單精度浮點型 | 6位有效數字 |
double | 雙精度浮點數 | 10有效數字 |
long double | 擴展精度浮點數 | 10有效數字 |
(1) c++語言的一些規則
一個int至少和一個short一樣大,一個long至少和一個int一樣大,一個long long至少和一個long一樣大。
(2) 帶符號類型和無符號類型
通過在類型名之前添加unsigned來得到無符號類型。
注意:字符型被分爲三種:char、signed char和unsigned char。類型char和類型signed char並不一樣。
字符型雖有三種類型,表現形式卻只有兩種:帶符號的和無符號的。類型char實際上會表現爲另外兩種中的一種,由編譯器決定。
建議:如果需要使用一個不大的整數,那麼明確指定它的類型是unsigned char或者signed char.執行浮點運算時選用double.
2、類型轉換(重點)
對象的類型定義了對象能包含的數據和能參與的運算。
(1) 給某種類型的對象強行賦給了另一種類型的值時
· 把一個非布爾值的算術值賦給布爾類型,初始值爲0則結果爲false,否則結果爲true
· 把一個布爾值賦給一個非布爾值時,初始值是false則結果爲0,初始值爲true則結果爲1。
· 把一個浮點數賦給整數類型時,結果直接去掉小數部分,只包含整數部分(不進行4舍5入,3.9則取3)。
· 把一個整數賦給浮點數時,結果爲小數部分記爲0(3則爲3.0)。如果該整數所佔的空超過浮點數,精度可能損失。
· 當給無符號類型賦值一個超出它表示的範圍時,結果是初始值對無符號類型表示數值總數取模後的餘數。當給其賦值一個 負值時,其結果爲總數加上該賦值(例子:給 unsigned char 變量賦值-1,則得到255,爲256+(-1)=255)
· 當給一個帶符號類型賦值一個超出它表示的範圍時,結果是未定義的。
· 把負數轉換成無符號數類似於直接給無符號數賦一個負值,結果等於這個負數加上無符號數的模。
/* 錯誤:變量u永遠也不會小於0,循環條件一直成立 */
for(unsigned int i=10; i>=0; --u)
std::cout << u << std::endl;
千萬不要混用有符號類型和無符號類型,常常會出現異常結果,因爲有符號類型會自動轉換成無符號值。
3、字面值常量
嚴格來說,十進制字面值不會是負值。-42的負十進制字面值,那個負號並不在字面值之內,它的作用僅僅是對字面值取負值而已。
轉義序列 : 如果反斜槓\後面跟着的八進制數字超過3個,只有前三個數字與\構成轉義序列(\1234表示兩個字符\123 和 4)。\x則要用到後面跟着的所有數字(\x1234表示一個16進制數)。
指定字面值的類型:
前綴 | 含義 | 類型 |
---|---|---|
u | Unicode 16 字符 | char16_t |
U | Unicode 32 字符 | char32_t |
L | 寬字符 | wchar_t |
u8 | UTF-8 (僅用於字符串字面值常量) | char |
後綴 | 最小匹配類型 |
---|---|
u or U | unsigned |
l or L | long |
ll or LL | long long |
後綴 | 類型 |
---|---|
f or F | float |
l or L | long double |
二、變量
變量提供一個具名的、可供程序操作的存儲空間。
1、變量定義
基本形式:類型說明符 一個或多個變量名
(1) 初始值:在對象創建時獲得了一個特定值,就說這個對象被初始化了
注意:在c++語言中,初始化和賦值是兩個完全不同的操作。初始化不是賦值,初始化的含義是創建變量時賦予其一個初始值,而賦值的含義是把對象的當前值擦掉,而以一個新值來替代。
(2)初始化的幾種不同形式
· int units_sold = 0;
· int units_sold = {0};
· int units_sold{0};
· int units_sold(0);
(3) 默認初始化
在定義時沒有指定初值,則變量被默認初始化。
如果是內置類型(不記得請往上看),定義與任何函數體之外的變量被初始化爲0。定義在函數體內部的內置類型變量將不被初始化,即其值未定義。
類的對象如果沒有顯示的初始化,則其值由類確定。
2、變量聲明和定義的關係
聲明:使得名字爲程序所知,一個文件如果想使用別處定義的名字則必須包含對那個名字的聲明。
定義:負責創建與名字關聯的實體。
如果想聲明一個變量而非定義它,就在變量名前添加關鍵字extern,而且不要顯示地初始化變量。
extern int i; // 聲明 i 而非定義 i
int j; // 聲明並定義j
// extern 語句如果包含初始值就不在是聲明,而變成定義了
extern double pi = 3.1416; // 定義
note : 變量只可以定義一次,但是可以被多次聲明。
三、複合類型
1、引用(必須初始化)---- ( & )
引用爲對象起了另外一個名字,引用類型引用另外一種類型。當定義引用時,程序把引用和它的初始值綁定在一起,而不是將初始值拷貝給引用,不能令引用重新綁定到另外一個對象上,所以引用必須初始化。
· 引用即別名----引用並非對象,相反的,引用只是爲一個已經存在的對象所以的另外一個名字。
· 引用只能綁定在對象上,而不能與字面值或某個表達式的計算結果綁定到一起,並且引用的類型和要與之綁定的對象的類型嚴格匹配。除了如下的兩個例外:
· 引用:變量的別名,指向同一個內存(1) 在初始化常量引用(const int &sp = i;)時允許用任意表達式作爲初始值,只要該表達式的結果能轉換成引用的類型即可。(後文有詳解)
(2) 站位置------2018-6-16(後補)
函數參數採用引用傳遞方式的有點如下:
1、保證不產生副本,提高傳遞的效率,節省內存開銷
2、可指定爲const,保證不改變實參的值,保證引用傳遞的安全性
2、指針
(1) 指針和引用的區別:
<a> 指針本身是一個對象,允許對指針賦值和拷貝,在生命週期可以指向多個不同的對象(不包括常量指針);而引用不是一 個對象,只是一個別名。
<b> 指針不需要在定義時必須賦初值;而引用必須初始化。
(2) 所有的指針的類型都要和他所指向的對象嚴格匹配,除了下面的兩個例外:
<1> 允許令一個指向常量的指針指向一個非常量對象。
<2> 佔位置------2018-6-16
(3) 有時候不明白一條語句到底改變了指針的值還是改變了指針所指對象的值,最好的辦法就是記住賦值永遠改變的是等號左側的對象。
·任何非0指針對應的條件值都是true.
(4) void *指針是一種特殊的指針類型,可用於存放任意對象的地址。一般拿其和別的指針作比較,作爲函數的輸入和輸出。但是注意:不能直接操作void * 指針所指的對象,必須對其進行顯示類型轉換才能進行操作。
· 引用本身不是一個對象,因此不能定義指向引用的指針。但指針是對象,所以存在對指針的引用。
tip:面對一條比較複雜的指針或引用的聲明語句時,從右向左閱讀有助於弄清楚它的真實含義。
四、const限定符詳解
當你希望定義一種值不能改變的變量時,可以使用const對該變量的類型進行限定(變成常量)。
// 把bufsize定義爲常量,即不能修改它的值
const int bufsize = 512;
bufsize = 520; // 錯誤:bufsize是一個整型常量,不能修改其值。
· 因爲常量創建後就不能改變其值,所以const對象必須初始化(引用也必須初始化).
· 與非const類型的所能參與的操作相比,const類型的對象只能執行不改變其內容的操作。
· 默認狀態下,const對象僅在文件內有效。當多個文件中出現了同名的const變量時,其實等同於在不同的文件中分別定義了獨立了的變量。當然,如果想在一個文件中定義,在多個文件中使用,則可以通過加extern關鍵字。但是需要注意:必須在定義和聲明都要加上extern關鍵字(與普通變量不一樣)。
// file_1.cc 定義並初始化了一個常量,該常量能被其他文件訪問。
extern const int bufsize = 10; // const對象必須初始化
// file_1.h 頭文件
extern const int bufsize; // 與file_1.cc中定義的bufsize是同一個
像上面的方法一樣,只要在其他文件想用bufsize則包含其頭文件即可。
1、const的引用------常量的引用
對常量的引用,不能被用作修改它所綁定的對象
const int ci = 1024;
const int &r1 = ci; // 正確:引用及其對象都是常量
r1 = 42; // 錯誤:r1是對常量的引用
int &r2 = ci; // 錯誤:試圖讓一個非常量引用指向一個常量對象(類型不匹配)
引用的類型必須與其所引用的對象的類型一致。但有兩個例外:
· 第一個就是在初始化常量引用時允許用任意的表達式作爲初始值,只要該表達式的結果能轉換成引用的類型即可。
請看例子分析:
double dval = 3.14; // double型變量
const int &ri = dval; // 用雙精度浮點型變量dval給整型常量引用進行初始化
一看上面的代碼,發現引用與其引用的對象的類型並不一致,一個爲整型常量引用,而一個爲雙精度變量,其實上面的代碼在編譯器編譯時就把第二行代碼改成了如下:
const int temp = dval; // 由雙精度浮點數生成一個臨時的整型變量
const int &ri = temp; // 讓ri綁定到這個臨時變量
所以在這個情況下,ri 綁定了一個臨時量對象。所謂臨時量對象就是當編譯器需要一個空間在暫存表達式的求值結果時臨時創建的一個未命名的對象。
對const的引用可能引用一個非const對象
int i = 520;
int &r1 = i; // 引用r1綁定i對象
const int &r2 = i; // 常量引用r2也綁定到i對象上,但不允許通過r2修改i的值
r1 = 0; // r1非常量引用,可以修改i的值
r2 = 0; // 錯誤:r2是一個常量引用。
對於常量引用,其實就是綁定到臨時量上,所以不能對臨時量進行修改其的操作。
2、指針和 const
指向常量的指針不能用於改變其所指對象的值。要想存放常量對象的地址,只能使用指向常量的指針。
const double pi = 3.14; // pi是個常量,它的值不能改變
double *ptr = π // 錯誤:ptr是一個普通指針
const double *cptr = π // 正確:cptr可以指向一個雙精度常量
*cptr = 42; // 錯誤:不能給*ptr賦值
指針的類型必須與其所指對象的類型一致。除了兩個例外。
第一個是允許令一個指向常量的指針指向一個非常量對象,和常量引用一樣,指向常量的指針沒有規定其所指的對象必須是一個常量,只是僅僅要求不能通過該指針改變對象的值,但並沒有規定那個對象的值不能通過其他方式改變。
· 常量指針必須初始化,而且一旦初始化完成,則其值(注意:指針的值指的是存放在指針中的地址)就不能改變。 *在const左邊表示該指針是一個常量指針(例子:int *const iptr = &i;),地址不可變,值可變,也就是指向的值可以變,但是它的指向不能改變。
· 指向常量的指針即*在const右邊(const int *ptr = &ci;),表示指向的值不能通過該指針來改變,而該指針的指向是可以改變的(存放在指針中的地址可以改變),地址可變,值不可變。
· 一個指針還可以是指向常量的常量指針,此時,則存放在指針中的地址不可變,指向的值也不可變。
3、頂層 const
用名詞頂層const表示指針本身是個常量,底層const表示指針所指的對象是一個常量。
int i= 0;
int *const p1 = &i; // 不能改變p1的值,這是一個頂層const(指針本事是常量)
const int ci = 42; // 不能改變ci的值,這是一個頂層const
const int *p2 = &ci; // 允許改變p2的值(可以改變指向),這是一個底層const
const int *const p3 = p2; // 靠右的const是頂層const,靠左的是底層const
const int &r = ci; // 用於聲明引用的const都是底層const
當執行拷貝時,常量是頂層還是底層有明顯區別。
執行拷貝操作時不會改變拷貝對象的值,拷入還是拷出的對象是否是常量沒什麼影響。但底層const的限制卻不能忽視,當執行對象的拷貝操作時,拷入和拷出的對象必須 具有相同的底層const,或者兩個對象的數據類型必須能夠轉換。一般非常量可以轉成常量,反之則不行。
4、類的常量成員函數
(1) 引入 this
// 有如下的結構體或者說類
struct Sales_data
{
std::string isbn() const
{
return bookNo;
}
std::string bookNo;
};
執行以下代碼
struct Sales_data total;
total.isbn();
在上面的調用中,當 isbn 返回 bookNo 時,實際上它隱式地返回total.bookNo。
成員函數通過一個名爲this的額外的隱式參數來訪問調用它的那個對象。當我們調用一個成員函數時,用請求該函數的對象地址初始化 this 。
任何對類成員函數的直接訪問都被直接看做 this 的隱式引用,上面就像書寫了 this->bookNo 一樣。
對於我們來說,this形參是隱式定義的。this 的目的總是指向“這個”對象,所以 this 是一個常量指針,即不允許改變this中保存的地址。
(2) const 成員函數
在上面的 isbn 函數後面緊跟着 const 關鍵字,此處 const 的作用主要是修改隱式 this 指針的類型。
默認情況下,this 的類型是指向類類型非常量版本的常量指針。意味着(在默認情況下)我們不能把 this 綁定到一個常量對象上,也就使得不能在一個常量對象上調用普通的成員函數。爲解決這個問題,c++語言允許把 const 關鍵字放在成員函數的參數列表之後,此時,緊跟在參數列表後面的 const 表示 this 是一個指向常量的常量指針。像這樣使用 const 的成員函數叫做常量成員函數。
// 所以上面的代碼,就可以等效爲下面的僞代碼
std::string Sales_data::isbn(const Sales_data *const this)
{
return this->bookNo;
}
小結:
const常用修飾普通變量和指針變量
修飾變量名,表示常變量,不能修改常量值
修飾*,表示該指針變量指向爲常量,不能通過該指針變量修改指向的內容
1、int * const p = &n; // 修飾指針變量名,表示常變量,不能修改常量值(可改內容,不可改地址,即不能對p進行賦值)
2、int const *q = &n; //這兩個具有相同的功能,const修飾*,
const int *p = &n; //表示指向常量,不能通過q指向的內容(可改地址,不可改內容,即不能對*P進行賦值)
注:當const在*左邊時如2,修飾指向的內容,即內容爲常量;
當const在右側時如1,修飾變量本身,即指向不可變
const的作用
1、阻止變量改變,通常聲明直接初始化,以後不能再改變
2、對於指針,可指定指針本身爲const,也可指定指向內容爲const,還可兩同時都爲const
3、在函數定義,修飾參數,表示在函數內部本能改變其值
4、對於類成員函數,指定爲const,則表明爲一個常成員函數,不能修改類的數據成員
(聲明樣式爲: 返回類型 <類標識符::>函數名稱(參數表) const)
5、對於類的成員函數,有時必須指定其返回值爲const類型,以使其返回值不爲“左值”