第二章
1.當(dāng)一個(gè)表達(dá)式中既有無(wú)符號(hào)數(shù)又有int值時(shí),int值會(huì)轉(zhuǎn)換成無(wú)符號(hào)數(shù)。
unsigned u = 10;
int i = -42;
std::cout << u+i <<std::endl;//如果int占32位,輸出4294967264
2.如果兩個(gè)字符串字面值位置緊鄰且僅由空格,縮進(jìn)和換行符分隔,則它們實(shí)際上是一個(gè)整體。
std::cout << "a really,really long string literal “
"that spans two lines " << std:: endl;
3.列表初始化
int units_sold = 0;
int units_sold = {0};
int units_sold{0};
int units_sold(0);
上面四條語(yǔ)句都可以做到初始化一個(gè)int變量。用花括號(hào)來(lái)初始化變量的方式稱為列表初始化。
初始化不是賦值,初始化的含義是創(chuàng)建變量時(shí)賦予其一個(gè)初始值,而賦值的含義是把對(duì)象的當(dāng)前值擦除,而以一個(gè)新值來(lái)替代。
4.默認(rèn)初始化
如果是內(nèi)置類型的變量未被顯式初始化,它的值由定義的位置決定。定義于任何函數(shù)體之外的變量被始化為0.定義在函數(shù)體內(nèi)部的內(nèi)置類型變量將不被初始化。
定義于函數(shù)體內(nèi)的內(nèi)置類型的對(duì)象如果沒(méi)有初始化,則其值未定義。類的對(duì)象如果沒(méi)有顯式地初始化,則其值由類確定。
5.變量聲明與定義
為了支持分離式編譯,C++語(yǔ)言將聲明和定義區(qū)分開(kāi)來(lái)。聲明 讓名字為程序所知,一個(gè)文件如果想使用別處定義的名字則必須包含對(duì)那個(gè)名字的聲明。而定義 負(fù)責(zé)創(chuàng)建與名字關(guān)聯(lián)的實(shí)體。變量聲明規(guī)定了變量的類型和名字,而定義除此之外,還申請(qǐng)存儲(chǔ)空間,也可能會(huì)為變量賦一個(gè)初始值。
extern int i; //聲明i而不是定義i
int j; //聲明并定義j
6.作用域
作用域能彼此包含,被包含的作用域稱為內(nèi)層作用域,包含著別的作用域的作用域稱為外層作用域。
作用域中一旦聲明了某個(gè)名字,它所嵌套著的所有作用域中都能訪問(wèn)該名字。同時(shí)允許在內(nèi)層作用域中重新定義外層作用域已有的名字。
7.復(fù)合類型
一條聲明語(yǔ)句由一個(gè)基本數(shù)據(jù)類型和緊隨其后一個(gè) 聲明符 列表組成。
- 引用 為對(duì)象起了另外一個(gè)名字,引用類型引用另外一種類型。通過(guò)將聲明符寫(xiě)成&d的形式來(lái)定義引用類型,其中d是聲明的變量名。
int ival = 1024;
int &refVal = ival; //refVal指向ival(是ival的另一個(gè)名字)
int &refVal2; //報(bào)錯(cuò):引用必須被初始化。
一般在初始化變量時(shí),初始值會(huì)被拷貝到新建的對(duì)象中。但是在定義引用時(shí),程序把引用和它的初始值 綁定 在一起,而不是將初始值拷貝給引用。因?yàn)闊o(wú)法令引用重新綁定到另外一個(gè)對(duì)象,因此引用必須初始化。引用并非一個(gè)對(duì)象,只是一個(gè)已經(jīng)存在的對(duì)象所起的另外一個(gè)名字。
引用只能綁定在對(duì)象上,而不能與字面值或某個(gè)表達(dá)的計(jì)算結(jié)果綁定在一起。
除了兩種例外情況,其他所有引用的類型都要和與之綁定的對(duì)象嚴(yán)格匹配。
第一種:在初始化常量引用時(shí)允許用任意表達(dá)式作為初始值,只要該表達(dá)式的結(jié)果能轉(zhuǎn)換成引用的類型即可。
int i = 42;
const int &r1 = i;
const int &r2 = 42;
const int &r3 = r1 * 2;
double dval = 3.14;
const int &ri = dval;
int &r4 = r1 * 2; //error
編譯器在這里把代碼變成
const int temp = dval;
const int &ri = temp;
即ri綁定了一個(gè)臨時(shí)量對(duì)象。臨時(shí)量對(duì)象就是當(dāng)編譯器需要一個(gè)空間來(lái)暫存表達(dá)式的求值結(jié)果時(shí)臨時(shí)創(chuàng)建的一個(gè)未命名的對(duì)象。(也叫臨時(shí)量)
-
指針 是”指向“另外一種類型的復(fù)合類型。相比于引用:指針本身就是一個(gè)對(duì)象,允許對(duì)指針賦值和拷貝,而且在指針的生命周期內(nèi)它可以先后指向幾個(gè)不同的對(duì)象;指針無(wú)須在定義時(shí)賦初值。
空指針的生成方法:
int *p1 = nullptr;
int *p2 = 0;
int *p3 = NULL; //新標(biāo)準(zhǔn)下最好使用nullptr,避免使用NULL;
除了兩種例外情況,其他所有指針的類型都要和它所指向的對(duì)象嚴(yán)格匹配。
第一種:允許令一個(gè)指向常量的指針指向一個(gè)非常量對(duì)象 。
void型指針可用于存放任意對(duì)象的地址。不能直接操作void指針?biāo)傅膶?duì)象,因?yàn)椴⒉恢肋@個(gè)對(duì)象到底是什么類型,無(wú)法確定能在這個(gè)對(duì)象上執(zhí)行哪些操作。(可以與其它指針比較,作為函數(shù)的輸入或輸出,或者賦給另外一個(gè)void*指針。)
面對(duì)一條比較復(fù)雜的指針或引用的聲明語(yǔ)句時(shí),從右向左閱讀有助于弄清楚它的真實(shí)含義
8.const限定符
const類型的對(duì)象只能執(zhí)行不改變其內(nèi)容的操作。
如果想在多個(gè)文件之間共享const對(duì)象,必須在變量的定義之前添加extern關(guān)鍵字。
對(duì)const的引用可能引用一個(gè)并非const的對(duì)象。對(duì)const的引用僅對(duì)引用可參與的操作做出了限定,對(duì)于引用的對(duì)象本身是不是一個(gè)常量限定。因此對(duì)象也可能是個(gè)非常量,允許通過(guò)其他途徑改變它的值。
int i = 42;
int &r1 = i;
const int &r2 = i;
r1 = 0;
r2 = 0; //error
上面代碼雖然不允許通過(guò)r2修改i的值,但是可以通過(guò)r1修改,或者直接給i賦值。
指針和const:類似于對(duì)const的引用,指向常量的指針不能用于改變其所指對(duì)象的值。要存放常量對(duì)象的地址,只能使用指向常量的指針。
const double pi = 3.14;
double *ptr = π //error
const double *cptr = π
*cptr = 42; //error
允許一個(gè)指向常量的指針指向一個(gè)非常量對(duì)象:
double dval = 3.14;
cptr = &dval; //正確,但是不能通過(guò)cptr來(lái)改變dval的值。
常量指針(const pointer):常量指針必須初始化,一旦初始化完成,它的值(即存放的地址)就不能再改變了。
int errNumb = 0;
int *const curErr = &errNumb; //curErr將一直指向errNumb
const double pi = 3.14159;
const double *const pip = π //pip是一個(gè)指向常量對(duì)象的常量指針。
用名詞 * 頂層const(top-level const) 表示指針本身是個(gè)常量,底層const(low-level const)*表示指針?biāo)傅膶?duì)象是一個(gè)常量。
更一般的,頂層const可以表示任意的對(duì)象是常量,底層const則與指針和引用等復(fù)合類型部分有關(guān)。比較特殊的是指針類型既可以是頂層const也可以是底層const。
int i = 0;
int *const p1 = &i; //不能改變p1的值,頂層const;
const int ci = 42; //不能改變ci的值,頂層const;
const int *p2 = &ci;//允許改變p2的值,這是一個(gè)底層const;
const int *const p3 = p2; //右邊的const是頂層const,左邊的是底層const;
const int &r = ci; //用于聲明引用的const都是底層const;
9.constexpr與常量表達(dá)式
常量表達(dá)式是指值不會(huì)改變并且在編譯過(guò)程就能得到計(jì)算結(jié)果的表達(dá)式。(如字面值)
一個(gè)對(duì)象(或表達(dá)式)是不是常量表達(dá)式由它的數(shù)據(jù)類型和初始值共同決定。
const int max_files = 20; //max_files是常量表達(dá)式
const int limit = max_files + 1; //limit是常量表達(dá)式
int staff_size =27; //staff_size不是,因?yàn)閿?shù)據(jù)類型只是一個(gè)普通int而非const int
const int sz = get_size(); //sz的具體值在運(yùn)行時(shí)獲得,幫也不是。
C++11新標(biāo)準(zhǔn)規(guī)定,允許將變量聲明為constexpr類型,以便由編譯器來(lái)驗(yàn)證變量的值是否是一個(gè)常量表達(dá)式。聲明為constexpr的變量一定是一個(gè)常量,而且必須用常量表達(dá)式初始化。
constexpr int sz = size();//只有當(dāng)size是一個(gè)constexpr函數(shù)時(shí),才是一條正確聲明語(yǔ)句。
指針與constexpr:限定符constexpr僅對(duì)指針有效,與指針?biāo)傅膶?duì)象無(wú)關(guān):
const int *p = nullptr; //p是一個(gè)指向整形常量的指針; constexpr int *q = nullptr; //q是一個(gè)指向整數(shù)的常量指針
10.類型別名:
- 傳統(tǒng)方法用關(guān)鍵字typedef定義類型別名。
typedef double wages;//wages是double的同義詞。 - 新標(biāo)準(zhǔn)規(guī)定了一種新方法,使用別名聲明 來(lái)定義類型的別名:
using SI = Sales_item; //SI是Sales_item的同義詞。
typedef char *pstring;
const pstring cstr = 0; //cstr是指向char的常量指針
const pstring *ps; //ps是一個(gè)指針,它的對(duì)象是指向char的常量指針(指針的指針)。
const是對(duì)給定類型的修飾,pstring實(shí)際上是指向char的指針,因此,const pstring 就是指向char的常量指針,而不指向常量字符的指針。
即不能把類型別名替換成它本來(lái)的樣子去理解。
11.auto類型說(shuō)明符
C++11標(biāo)準(zhǔn)引入了auto類型說(shuō)明符,用它就能讓編譯器替我們?nèi)シ治霰磉_(dá)式所屬的類型,編譯器通過(guò)初始值來(lái)推算變量的類型。所以,auto定義的變量必須有初始值
auto也能在一條語(yǔ)句中聲明多個(gè)變量,但一條聲明語(yǔ)句只能有一個(gè)基本數(shù)據(jù)類型,所 所有變量的初始基本數(shù)據(jù)類型都必須一樣
auto sz = 0, pi = 3.14; //error
auto一般會(huì)忽略掉頂層const,同時(shí)底層const會(huì)保留下來(lái),比如當(dāng)初始值是一個(gè)指向常量的指針時(shí):
int i = 0;
const int ci = i,&cr = ci;
auto b = ci; //b是一個(gè)整數(shù)(ci的頂層const特性被忽略了,即b不是常量)。
auto c = cr; //c是一個(gè)整數(shù)
auto d = &i; //d是一個(gè)整形指針
auto e = &ci; //e是一個(gè)指向整數(shù)常量的指針(對(duì)常量對(duì)象取地址是一種底層const)
12.decltype類型指示符
C++11新標(biāo)準(zhǔn)引入的第二種類型說(shuō)明符,作用是選擇并返回操作數(shù)的數(shù)據(jù)類型。編譯器分析表達(dá)式并得到它的類型,卻不實(shí)際計(jì)算表達(dá)式的值:
delctype(f()) sum = x; //sum的類型就是函數(shù)f的返回類型。
編譯器并不實(shí)際調(diào)用函數(shù)f,而是使用當(dāng)調(diào)用發(fā)生時(shí)f的返回值類型作為sum的類型。
decltype與引用
int i = 42,*p = &i, &r = i;
decltype(r + 0) b; //正確:加法的結(jié)果是int,因此b是一個(gè)(未初始化的)int
decltype(*p) c; //錯(cuò)誤:c是int&,必須初始化。
即如果表達(dá)式的內(nèi)容是解引用操作,則decltype將得到引用類型。(解引用指針可以得到指針?biāo)傅膶?duì)象,而且還能給這個(gè)對(duì)象賦值。因此,是int&)
decltype的表達(dá)式如果是加上了括號(hào)的變量,結(jié)果將是引用。
decltype((i)) d; //error
13.自定義數(shù)據(jù)類型struct
不要忘了在struct類體后面加分號(hào)!
新標(biāo)準(zhǔn)規(guī)定,可以為數(shù)據(jù)成員提供一個(gè) 類內(nèi)初始值。創(chuàng)建對(duì)象時(shí),初始值將用于初始化數(shù)據(jù)成員,沒(méi)有初始值的成員,將被默認(rèn)初始化。
預(yù)處理器概述
確保頭文件多次包含仍能安全工作的常用技術(shù)是 預(yù)處理器 。從C繼承而來(lái),而C++還會(huì)用到一項(xiàng)預(yù)處理功能是 頭文件保護(hù)符 ,它依賴于預(yù)處理變量,而預(yù)處理變量有兩種狀態(tài):已定義,未定義。
#define 指令把一個(gè)名字設(shè)定為預(yù)處理變量,而#ifdef與#ifndef指令檢查狀態(tài)。當(dāng)檢查結(jié)果為真時(shí),執(zhí)行后續(xù)操作直到遇到#endif指令。#ifndef SALES_DATA_H #define SALES_DATA_H #include <string> struct Sales_data {
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
endif
預(yù)處理器無(wú)視C++中關(guān)于作用域的規(guī)則。