1. 程式人生 > >C++Primer筆記+練習答案-第二章

C++Primer筆記+練習答案-第二章

筆記部分比較混亂只記錄自己認為重要的點。

筆記

2.1 基本內建型別

2.1.1 算術型別

詳見p30表格

2.1.2 型別轉換

非布林值轉換為布林值時,非零為true,0為false。
布林值在算術表達中,true為1,false為0,不宜用在算術值中使用布林值。

2.1.3 字面值常量

每個字面值常量都對應一種資料型別,字面值常量的形式和值決定了它的資料型別
如 :
42 整型十進位制
042 整型八進位制
0x42 整型十六進位制
42.0 浮點型
“42” 字串字面值
‘4’ 字元字面值
可通過新增字首和字尾改變整型、浮點數、和字元字面值的型別

42 整型 int
42L 整型 long int
42ULL 整型 usigned long long
42.0L 浮點數 long double
布林字面值:false和true

2.2 變數

變數提供一個劇名的、可供程式操作的儲存空間。

2.2.1 變數定義

型別說明符 變數名(一個或多個)
如:

int a,b;

初始化
初始化不等同於賦值。
列表初始化:當用於內建型別的變數時,這種初始化形式有一個重要特點:如果我們使用列表初始化且初始值存在丟失資訊的風險時,編譯器將報錯:

2.2.2 宣告與定義的關係

宣告(declaration)使得名字為程式所知
定義(definition)負責建立與名字關聯的實體

extern int i;//宣告而非定義
int j;//宣告並定義了j

變數只能被定義一次,但可以被多次宣告。

2.2.3 識別符號

識別符號(identifier

)由字母、數字和下劃線組成,必須以字母和下劃線開頭。長度沒有限制,大小寫字母敏感。
C++關鍵字詳見p43

2.2.3 名字的作用域

全域性作用域
塊作用域

2.3 複合型別

符合型別是指基於其他型別定義的型別。

2.3.1 引用

引用即別名
引用一旦初始化完成,引用將和它的初始物件一直繫結在一起,無法重新繫結到另一物件
引用必須初始化
引用本身不是一個物件,不能定義引用的引用

2.3.2 指標

指標必須與它指向的物件嚴格匹配

空指標
幾個生成空指標的方法

int *p1 = nullptr; // equivalent to int *p1 = 0;
int *p2 = 0;       // directly initializes p2 from the literal constant 0
// must #include cstdlib int *p3 = NULL; // equivalent to int *p3 = 0;

指標在條件表示式中,如果指標的值為0條件取false,否則為true。

void*指標
void指標可以存放任意物件的地址。
void
指標可以用於和別的指標比較,作為函式的輸入或輸出,或者賦給另一個void 指標,但不能直接操作所指的物件,因為不知道物件到底是什麼型別,可以做哪些操作。

2.3.3 理解符合型別的宣告

理解一個變數型別到底是什麼,最簡單的方法就是從右往左讀該變數的定義。離變數最近的符號(如符號&)對變數的型別有最直接的影響。
如:

int *p;
int *&r=p;

離r最近的符號是&,所以r是一個引用,宣告符的其餘部分用以確定r引用的型別是什麼,符號*說明r引用的是指標。最後,宣告的基本資料型別部分指出r引用的是一個int指標。

2.4 const限定符

const物件一旦建立後其值就不能再改變,所以const物件必須初始化。初始值可以是任意複雜的表示式。
預設狀態下,const物件僅在檔案內有效
當多個檔案出現同名的const變數時,等同於在不同檔案中分別定義了獨立的變數。
如果不希望編譯器為每個檔案分別生成獨立的變數,可以使用extern關鍵字。

2.4.1 const的引用

把引用繫結到const物件上,稱之為常量引用(reference to const)
對常量的引用不能改變它所繫結物件的值。、

初始化和對const的引用
常量引用可以繫結非常量物件、字面值、甚至是個一般表示式。

int i = 42;
const int &r1 = i;      // we can bind a const int& to a plain int object
const int &r2 = 42;     // ok: r1 is a reference to const
const int &r3 = r1 * 2; // ok: r3 is a reference to const
int &r4 = r * 2;        // error: r4 is a plain, non const reference
double dval = 3.14;
const int &ri = dval;

編譯器會把程式碼變成如下形式:

const int temp = dval;   // create a temporary const int from the double
const int &ri = temp;    // bind ri to that temporary

ri綁定了一個臨時量物件

對const的引用可能引用一個非const的物件
必須認識到, 常量引用僅對可參與的操作做出了限定, 對於引用的物件本身是不個常量未作限定。 因為物件也可能是個非常量, 所以允許通過其他途徑改變它的值:

int i = 42;
int &r1 = i;          // r1 bound to i
const int &r2 = i;    // r2 also bound to i; but cannot be used to change i
r1 = 0;               // r1 is not const; i is now 0
r2 = 0;               // error: r2 is a reference to const

2.4.2 指標和const

想要存放常量物件的地址,只能使用指向常量的指標
允許令一個指向常量的指標指向一個非常量物件,但該指標不能用於改變其所指物件的值。
const指標
常量指標必須初始化

2.4.3 頂層const

頂層const(top-level const表示指標本身時個常量
底層const(low-level const表示指標所指的物件時一個常量
更一般的,頂層const 表示任意物件時常量,這一點對任意資料型別都適用
底層const則與指標和引用等符合型別有關。

int i = 0;
int *const p1 = &i;  // we can't change the value of p1; const is top-level
const int ci = 42;   // we cannot change ci; const is top-level
const int *p2 = &ci; // we can change p2; const is low-level
const int *const p3 = p2; // right-most const is top-level, left-most is not
const int &r = ci;  // const in reference types is always low-level

當拷貝物件時,頂層const不受影響。另一方面,底層const的限制卻不能忽略。

2.4.4 常量表達式

常量表達式(const expression)是指值不會改變並且在編譯過程中就能得到計算結果的表示式。
constexpt變數
一般來說,如果認定變數是一個常量表達式,那就把它生命成constexpr型別。宣告成constexpr的變數一定是一個常量,而且必須用常量表達式初始化。

constexpr int mf = 20;        // 20 is a constant expression
constexpr int limit = mf + 1; // mf + 1 is a constant expression
constexpr int sz = size();    // ok only if size is a constexpr function

指標和constexpr
限定符constexpr只對指標有效,與所指向的物件無關

const int *p = nullptr;     // p is a pointer to a const int
constexpr int *q = nullptr; // q is a const pointer to int

constexpr把它所定義的物件置為了頂層const

2.5 處理型別

2.5.1 類型別名

傳統方法使用typedef關鍵字

typedef double wages;   // wages is a synonym for double
typedef wages base, *p; // base is a synonym for double, p for double*

新標準使用using宣告

using SI = Sales_item;  // SI is a synonym for Sales_item

2.5.2 auto型別說明符

auto能讓編譯器替我們去分析表示式所屬的型別。
使用auto也能在一條表示式中宣告多個變數。因為一條宣告語句只能有一個基本資料型別,所以該語句中所有變數的初始基本資料型別都必須一樣。

複合型別、常量和auto
當引用被用作初始值時,真正參與初始化的其實是引用物件的值,此時編譯器以引用的物件作為auto的型別

int i = 0, &r = i;
auto a = r;  // a is an int (r is an alias for i, which has type int)

auto一般會忽略掉頂層const,同時底層const會保留下來,比如當初始值是一個指向常量的指標時:

const int ci = i, &cr = ci;
auto b = ci;  // b is an int (top-level const in ci is dropped)
auto c = cr;  // c is an int (cr is an alias for ci whose const is top-level)
auto d = &i;  // d is an int*(& of an int object is int*)
auto e = &ci; // e is const int*(& of a const object is low-level const)

如果希望推斷出的auto型別是一個頂層const,需要明確指出:

const auto f = ci; // deduced type of ci is int; f has type const int

引用型別也可以設定為auto,原來的初始化規則仍然適用

2.5.3 decltype型別指示符

decltype的作用是選擇並返回運算元的資料型別
如果decltype使用的表示式是一個變數,則decltype返回該變數的型別(包括頂層const和引用在內):

const int ci = 0, &cj = ci;
decltype(ci) x = 0; // x has type const int
decltype(cj) y = x; // y has type const int& and is bound to x
decltype(cj) z;     // error: z is a reference and must be initialized

引用從來都作為所指的物件的同義詞出現,只有用decltpye處是一個例外

decltype和引用
有些表示式將向的decltype返回一個引用型別,一般來說當這種情況發生時,意味著該表示式的結果物件能作為一條賦值語句的左值:

int i = 42, *p = &i, &r = i;
decltype(r + 0) b;  // ok: addition yields an int; b is an (uninitialized) int
decltype(*p) c;     // error: c is int& and must be initialized

如果表示式的內容是解引用操作,則decltype將得到引用型別。

decltype((variable)(注意是雙層括號)的結果永遠是引用,而decltype(variable)結果只有當variable本身就是一個引用時才是引用。

// decltype of a parenthesized variable is always a reference
decltype((i)) d;    // error: d is int& and must be initialized
decltype(i) e;      // ok: e is an (uninitialized) int

2.6 自定義資料結構

關鍵字struct

預編譯處理
預處理變數無視C++語言中關於作用域的規則
標頭檔案保護,防止重複包含。

練習答案

Exercise 2.1: What are the differences between int, long, long long,
and short? Between an unsigned and a signed type? Between a float and
a double?
略,詳見書。
Exercise 2.2: To calculate a mortgage payment, what types would you use
for the rate, principal, and payment? Explain why you selected each type.

選擇double較合適,利率本金付款皆不是整數,所以不選取整數型別;double和float相比精度更高,運算成本相差無幾。double的所容納的數一般而言足夠,不必要選long double。
Exercise 2.3: What output will the following code produce?

unsigned u = 10, u2 = 42;
std::cout << u2 - u << std::endl;//42-10,32
std::cout << u - u2 << std::endl;//無符號數運算,10-32算術結果-32,相當於-32加上2的32次方,結果4294967264
int i = 10, i2 = 42;
std::cout << i2 - i << std::endl;//int 整型間的運算32
std::cout << i - i2 << std::endl;//同上-32
std::cout << i - u << std::endl;//無符號和有符號數的運算,為0
std::cout << u - i << std::endl;//同上0

Exercise 2.4: Write a program to check whether your predictions were
correct. If not, study this section until you understand what the problem is.

#include<iostream>
int main(){
	unsigned u = 10, u2 = 42;
	std::cout << u2 - u << std::endl;
	std::cout << u - u2 << std::endl;
	int i = 10, i2 = 42;\
	std::cout << i2 - i << std::endl;
	std::cout << i - i2 << std::endl;
	std::cout << i - u << std::endl;
	std::cout << u - i << std::endl;
	return 0;
} 

Exercise 2.5: Determine the type of each of the following literals. Explain the differences among the literals in each of the four examples:

(a) ‘a’, L’a’, “a”, L"a"
字元字面值char
寬字元字面值wchar_t
字串字面值string
寬字串字面值

(b) 10, 10u, 10L, 10uL, 012, 0xC
整型十進位制int,
無符號十進位制整型unsigned int,
十進位制整型long,
十進位制無符號整型usigned long,
八進位制整型
十六進位制整型

© 3.14, 3.14f, 3.14L
浮點數double
浮點數float
浮點數long double

(d) 10, 10u, 10., 10e-2
十進位制整型 int
無符號整型 usigned int
浮點數double
浮點數double

Exercise 2.6: What, if any, are the differences between the following
definitions:

int month = 9, day = 7;
int month = 09, day = 07;
第一行為十進位制,第二行0開頭為八進位制字面值,出現9會報錯。

Exercise 2.7: What values do these literals represent? What type does each have?

(a) “Who goes with F\145rgus?\012”
Who goes with Fergus?字串string
(b) 3.14e1L
31.4 long double
(c ) 1024f
float
(d) 3.14L
3.14long double

Exercise 2.8: Using escape sequences, write a program to print 2 M followed by a newline. Modify the program to print 2, then a tab, then an M, followed by a newline.

#include <iostream>
int main()
{
    std::cout << "2\115\012";
    std::cout << "2\t\115\012"; 
    return 0;
}

Exercise 2.9: Explain the following definitions. For those that are illegal, explain what’s wrong and how to correct it.

(a) std::cin >> int input_value;
錯誤
(b) int i = { 3.14 };
列表初始化,會丟失精度,錯誤
© double salary = wage = 9999.99;
錯誤
(d) int i = 3.14;
正確但會丟失精度

Exercise 2.10: What are the initial values, if any, of each of the following variables?

std::string global_str;//空字串
int global_int;//全域性變數 0
int main()
{
    int local_int;//為賦值
    std::string local_str;//空字串
}

Exercise 2.11: Explain whether each of the following is a declaration or a
definition:

a) extern int ix = 1024;
定義
b) int iy;
定義
c) extern int iz
宣告

Exercise 2.12: Which, if any, of the following names are invalid?

(a) int double = 3.14;
非法,double是關鍵字
(b) int _;
合法
© int catch-22;
非法
(d) int 1_or_2 = 1;
非法,只能以下劃線或者字母開頭
(e) double Double = 3.14;
合法

Exercise 2.13: What is the value of j in the following program?

int i = 42;
int main()
{
    int i = 100;
    int j = i;
}

j=100

Exercise 2.14: Is the following program legal? If so, what values are printed?

int i = 100, sum = 0;
for (int i = 0; i != 10; ++i)
     sum += i;
std::cout << i << " " << sum << std::endl;

100 45

Exercise 2.15: Which of the following definitions, if any, are invalid? Why?

(a) int ival = 1.01;
合法,但會丟失精度
(b) int &rval1 = 1.01;
不合法,不能定義字面值引用
© int &rval2 = ival;
合法
(d) int &rval3;
不合法,引用必須初始化

Exercise 2.16: Which, if any, of the following assignments are invalid? If they are valid, explain what they do.
int i = 0, &r1 = i; double d = 0, &r2 = d;

(a) r2 = 3.14159;
合法,對d賦值 d的值變為3.14159
(b) r2 = r1;
合法,將i的值賦給d d的值為0
© i = r2;
合法,將d的值賦給i,可能丟失精度
(d) r1 = d;
合法

Exercise 2.17: What does the following code print?

int i, &ri = i;
i = 5; ri = 10;
std::cout << i << " " << ri << std::endl;

10 10

Exercise 2.18: Write code to change the value of a pointer. Write code to
change the value to which the pointer points.

Exercise 2.19: Explain the key differences between pointers and references.
引用只是變數的別名,本身不是物件,必須初始化,初始化後不能改變繫結的物件。
指標本身是物件,能對本身賦值,可以不初始化,並且可以改變指向的物件。

Exercise 2.20: What does the following program do?

int i = 42;
int *p1 = &i;
*p1 = *p1 * *p1;

計算i*i並將值賦給i

Exercise 2.21: Explain each of the following definitions. Indicate whether any are illegal and, if so, why.
int i = 0;
(a) double* dp = &i;
不合法dp是指向double的指標,不能指向i
(b) int *ip = i;
不合法。不能將int 的值賦給指標(int 的值為0也不可以)
© int *p = &i;
合法p是指向i的指標

Exercise 2.22: Assuming p is a pointer to int, explain the following code:

if (p ) // ….p是空指標是為false 否則為true
if (*p) // …p所指向的物件為0是為false ,否則為true

Exercise 2.23: Given a pointer p, can you determine whether p points to a
valid object? If so, how? If not, why not?
不能知道

Exercise 2.24: Why is the initialization of p legal but that of lp illegal?

int i = 42; void p = &i; long lp = &i;
p是void
*指標,可以指向任意物件
lp是指向long的指標,不能指向i

Exercise 2.25: Determine the types and values of each of the following variables.

(a) int* ip, i,&r = i;
ip指向int 的指標,i是int型,r是i的引用
(b) int i, ip = 0;
i是int 整型ip是空指標
© int
ip, ip2;
ip是指向int的指標,ip2不是int整型

Exercise 2.26: Which of the following are legal? For those that are illegal,
explain why.

(a) const int buf;
不合法 const要初始化
(b) int cnt = 0;
合法
© const int sz = cnt;
合法
(d) ++cnt; ++sz;
前者合法後者不合法,const的值不能改變

Exercise 2.27: Which of the following initializations are legal? Explain why.

(a) int i = -1, &r = 0;
i合法,r不合法,不能對字面值引用
(b) int *const p2 = &i2;
合法p2是指向i2的常量指標
© const int i = -1, &r = 0;
合法r是常量的引用
(d) const int *const p3 = &i2;
合法
(e) const int *p1 = &i2;
合法
(f) const int &const r2;
不合法
(g) const int i2 = i, &r = i;
合法

Exercise 2.28: Explain the following definitions. Identify any that are illegal.

(a) int i, *const cp;
i是int整型,合法;cp是指向整型的常量指標,常量必須初始化,不合法。
(b) int *p1, *const p2;
p1是指向int的指標,合法;p2是指向int的常量指標,必須初始化,不合法
© const int ic, &r = ic;
不合法。同上。
(d) const int *const p3;
不合法
(e) const int *p;
合法

Exercise 2.29: Uing the variables in the previous exercise, which of the following assignments are legal? Explain why.

(a) i = ic;
合法,ic是頂層const
(b) p1 = p3;
不合法,p3是底層const
© p1 = & ic;
不合法,ic是const int,p1指向int
(d) p3 = & ic;
不合法,p3是常量指標
(e) p2 = p1;
不合法,p2是常量指標
(f) ic = *p3;
不合法,ic的值不能被改變

Exercise 2.30: For each of the following declarations indicate whether the object being declared has top-level or low-level const.

const int v2 = 0; int v1 = v2;
int *p1 = &v1, &r1 = v1;
const int *p2 = &v2, *const p3 = &i, &r2 = v2;
v2頂層const
p2底層const
p3既是頂層const也是底層const

Exercise 2.31: Given the declarations in the previous exercise determine whether the following assignments are legal. Explain how the top-level or low-level const applies in each case.
r1 = v2;合法
p1 = p2;不合法,p2是底層const
p2 = p1;合法
p1 = p3; 不合法
p2 = p3;合法

Exercise 2.32: Is the following code legal or not? If not, how might you make it legal?
int null = 0, *p = null;
不合法,改為
*p=nullptr;

Exercise 2.33: Using the variable definitions from this section, determine what happens in each of these assignments:

int i = 0, &r = i;
auto a = r; // a is an int (r is an alias for i, which has type int)
const int ci = i, &cr = ci;
auto b = ci; // b is an int (top-level const in ci is dropped)
auto c = cr; // c is an int (cr is an alias for ci whose const is top-level)
auto d = &i; // d is an int*(& of an int object is int*)
auto e = &ci; // e is const int*(& of a const object is low-level const)
const auto f = ci; // deduced type of ci is int; f has type const int
auto &g = ci; // g is a const int& that is bound to ci

a = 42; b = 42; c = 42;
d = 42; e = 42; g = 42;
abc合法,
d是int*,不合法
f是常量,不合法
g是常量引用,不合法

Exercise 2.34: Write a program containing the variables and assignments from the previous exercise. Print the variables before and after the assignments to check whether your predictions in the previous exercise were correct. If not, study the examples until you can convince yourself you know what led you to the wrong conclusion.

Exercise 2.35: Determine the types deduced in each of the following
definitions. Once you’ve figured out the types, write a program to see
whether you were correct.

const int i = 42;
auto j = i; const auto &k = i; auto *p = &i;
const auto j2 = i, &k2 = i;
Exercise 2.36: In the following code, determine the type of each variable
and the value each variable has when the code finishes:

int a = 3, b = 4;
decltype(a) c = a;
decltype((b)) d = a;
++c;
++d;

Exercise 2.37: Assignment is an example of an expression that yields a
reference type. The type is a reference to the type of the left-hand operand.
That is, if i is an int, then the type of the expression i = x is int&. Using
that knowledge, determine the type and value of each variable in this code:

int a = 3, b = 4;
decltype(a) c = a;
decltype(a = b) d = a;

Exercise 2.38: Describe the differences in type deduction between
decltype and auto. Give an example of an expression where auto and
decltype will deduce the same type and an example where they will deduce
differing types.

Exercise 2.39: Compile the following program to see what happens when
you forget the semicolon after a class definition. Remember the message for
future reference.
struct Foo { /* empty */ } // Note: no semicolon
int main()
{
return 0;
}

Exercise 2.40: Write your own version of the Sales_data class.

Exercise 2.41: Use your Sales_data class to rewrite the exercises in §
1.5.1 (p. 22), § 1.5.2 (p. 24), and § 1.6 (p. 25). For now, you should define
your Sales_data class in the same file as your main function.

Exercise 2.42: Write your own version of the Sales_data.h header and
use it to rewrite the exercise from § 2.6.2 (p. 76).