第 2 章
2.1
int、long、long long 和 short 表示整型類型,C++語(yǔ)言規(guī)定它們表示數(shù)的范圍 short \(\leq\) int \(\leq\) long \(\leq\) long long。其中,數(shù)據(jù)類型 long long 是在 C++11 中新定義的;
除去布爾型和擴(kuò)展的字符型之外,其它整型可以劃分為帶符號(hào)的(signed)和無符號(hào)的(unsigned)兩種。帶符號(hào)類型可以表示正數(shù)、負(fù)數(shù)和 0,無符號(hào)類型則僅能表示大于等于 0 的值。類型 int、short、long 和 long long 都是帶符號(hào)的,通過在這些類型名前添加 unsigned 就可以得到無符號(hào)類型,例如 unsigned long。類型 unsigned int 可以縮寫為 unsigned;
float 和 double 用于表示浮點(diǎn)數(shù),其中,float 表示單精度浮點(diǎn)數(shù),double 表示雙精度浮點(diǎn)數(shù)。執(zhí)行浮點(diǎn)數(shù)運(yùn)算選用 double,這是因?yàn)?float 通常精度不夠而且雙精度浮點(diǎn)數(shù)和單精度浮點(diǎn)數(shù)的計(jì)算代價(jià)相差無幾。事實(shí)上,對(duì)于某些機(jī)器來說,雙精度運(yùn)算甚至比單精度還快。long double 提供的精度在一般情況下是沒有必要的,況且它帶來的運(yùn)算時(shí)消耗也不容忽視。
附:單精度與雙精度是什么意思,有什么區(qū)別?
2.2
利率(rat):float,本金(principal):long long,付款(payment):long long。
利率一般是小數(shù)點(diǎn)后保留四位有效數(shù)字,float 合適;本金和付款使用最大的帶符號(hào)整型表示。
2.3
main.cpp
#include <iostream>int main() {std::cout << sizeof(unsigned) << std::endl;std::cout << sizeof(int) << std::endl;std::cout << "**************************" << std::endl;unsigned u = 10, u2 = 42;std::cout << u2 - u << std::endl;std::cout << u - u2 << std::endl;std::cout << "**************************" << 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; } 4 4 ************************** 32 4294967264 ************************** 32 -32 0 0Process finished with exit code 0 unsigned int 占 4 字節(jié) int 占 4 字節(jié) ************************** u2 - u = 42 - 10 = 32 u - u2 = 10 - 42 = unsigned(-32) = 4294967264 分析一: 32 的二進(jìn)制表示為 0000 0000 0000 0000 0000 0000 0010 0000 -32 的二進(jìn)制補(bǔ)碼表示為 1111 1111 1111 1111 1111 1111 1110 0000 也就是說 -32 的十六進(jìn)制表示為 FFFF FFE0 若將 FFFF FFE0 轉(zhuǎn)換為 unsigned int 類型,則高位的符號(hào)位全部成為了數(shù)值位,FFFF FFE0 按無符號(hào)數(shù)轉(zhuǎn)換成十進(jìn)制數(shù)為 4294967264。 分析二: 把負(fù)數(shù)轉(zhuǎn)換成無符號(hào)數(shù)類似于直接給無符號(hào)數(shù)賦一個(gè)負(fù)值,結(jié)果等于這個(gè)負(fù)數(shù)加上無符號(hào)數(shù)的模。 unsigned int 占 4 字節(jié),4 字節(jié)的無符號(hào)數(shù)模為 4294967296。 -32 + 4294967296 = 4294967264 ************************** i2 - i = 42 - 10 = 32 i - i2 = 10 - 42 = -32 當(dāng)一個(gè)算術(shù)表達(dá)式中既有無符號(hào)數(shù)又有 int 值時(shí),那個(gè) int 值就會(huì)轉(zhuǎn)換成無符號(hào)數(shù)。 i - u = unsigned(10) - 10 = 0 u - i = 10 - unsigned(10) = 0 分析: 有符號(hào)數(shù) 10 轉(zhuǎn)換成無符號(hào)數(shù) 10,因?yàn)?10 是正數(shù),符號(hào)位為 0,故轉(zhuǎn)換成無符號(hào)數(shù)時(shí),符號(hào)位不會(huì)對(duì)對(duì)應(yīng)無符號(hào)數(shù)產(chǎn)生副作用。Process finished with exit code 0 程序中 return 0 語(yǔ)句的副作用2.4
請(qǐng)看 2.3
2.5
(a) 'a', L'a', "a", L"a"
| 'a' | 字符字面值,類型是 char |
| L'a' | 寬字符字面值,類型是 wchar_t |
| "a" | 字符串字面值 |
| L"a" | 寬字符串字面值 |
(b) 10, 10u, 10L, 10uL, 012, 0xC
| 10 | 整型字面值,類型是 int |
| 10u | 無符號(hào)整型字面值,類型是 unsigned int |
| 10L | 整型字面值,類型是 long |
| 10uL | 無符號(hào)整型字面值,類型是 unsigned long |
| 012 | 八進(jìn)制整型字面值,類型是 int |
| 0xC | 十六進(jìn)制整型字面值,類型是 int |
(c) 3.14, 3.14f, 3.14L
| 3.14 | 浮點(diǎn)數(shù)字面值,類型是 double |
| 3.14f | 浮點(diǎn)數(shù)字面值,類型是 float |
| 3.14L | 浮點(diǎn)數(shù)字面值,類型是 long double |
(d) 10, 10u, 10., 10e-2
| 10 | 整型字面值,類型是 int |
| 10u | 無符號(hào)整型字面值,類型是 unsigned int |
| 10. | 浮點(diǎn)數(shù)字面值,類型是 double |
| 10e-2 | 浮點(diǎn)數(shù)字面值,類型是 double |
2.6
有區(qū)別
int month = 9, day = 7; // 9 和 7 是十進(jìn)制數(shù),正確的賦值 // 以 0 開始表示八進(jìn)制,八進(jìn)制各位范圍是 0 ~ 7,賦值不合法 int month = 09, day = 07;2.7
| "Who goes with F\145rgus?\012" | 字符串字面值,包含兩個(gè)八進(jìn)制轉(zhuǎn)義序列 |
| 3.14e1L | 浮點(diǎn)數(shù)字面值,類型是 long double |
| 1024f | 浮點(diǎn)數(shù)字面值,類型是 float |
| 3.14L | 浮點(diǎn)數(shù)字面值,類型是 long double |
2.8
分別寫了八進(jìn)制和十六進(jìn)制的版本
ASCII
#include <iostream>int main() {std::cout << "-------- Oct --------" << std::endl;std::cout << "\62\115\12";std::cout << "---------------------" << std::endl;std::cout << "\62";std::cout << "\11";std::cout << "\115";std::cout << "\12";std::cout << "-------- Hx ---------" << std::endl;std::cout << "\x32\x4d\xa";std::cout << "---------------------" << std::endl;std::cout << "\x32";std::cout << "\x9";std::cout << "\x4d";std::cout << "\xa";return 0; } // 運(yùn)行結(jié)果 -------- Oct -------- 2M --------------------- 2 M -------- Hx --------- 2M --------------------- 2 MProcess finished with exit code 02.9
// (a) // std::cin >> int input_value; // Error: variable must be defined before using for input int input_value; std::cin >> input_value;// (b) // int i = { 3.14 }; // Error: loss information in list initialization double d = { 3.14 }; // OK double d2 = { 3 }; // OK// (c) // double salary = wage = 9999.99; // Error double salary, wage; salary = wage = 9999.99;// (d) int i2 = 3.14; // OK, `i2` is 32.10
#include <iostream>std::string global_str; int global_int; int main () {int local_int;std::string local_str;std::cout << "global_str = " << global_str << std::endl;std::cout << "global_int = " << global_int << std::endl;std::cout << "local_str = " << local_str << std::endl;std::cout << "local_int = " << local_int << std::endl;return 0; } // 運(yùn)行結(jié)果 global_str = global_int = 0 local_str = local_int = -406316616Process finished with exit code 02.11
extern int ix = 1024; // 定義 int iy; // 聲明并定義 iy extern int iz; // 聲明 iz 而非定義 iz2.12
int double = 3.14; // 錯(cuò)誤,c++關(guān)鍵字 double 不能作為標(biāo)識(shí)符 int _; // 正確 int catch-22; // 錯(cuò)誤,標(biāo)識(shí)符由數(shù)字、字母和下劃線組成 int 1_or_2 = 1; // 錯(cuò)誤,標(biāo)識(shí)符必須以字母或下劃線開頭 double Double = 3.14; // 正確,標(biāo)識(shí)符長(zhǎng)度沒有限制,大小寫敏感2.13
程序中 j 的值是 100
2.14
#include <iostream>int main () {int i = 100, sum = 0;for (int i = 0; i != 10; ++i)sum += i;std::cout << i << " " << sum << std::endl;return 0; } // 運(yùn)行結(jié)果 100 45Process finished with exit code 02.15
#include <iostream>int main () {// 正確,隱式的將 1.01 轉(zhuǎn)換成 1int ival= 1.01;// 錯(cuò)誤,除 2.4.1 節(jié)(第 55 頁(yè))和 15.2.3 節(jié)(第 534 頁(yè))將// 要介紹的兩種例外情況,引用的類型都要和與之綁定的對(duì)象嚴(yán)格匹// 配。而且,引用只能綁定在對(duì)象上,而不能與字面值或某個(gè)表達(dá)式的// 計(jì)算結(jié)果綁定在一起,相關(guān)原因?qū)⒃?2.4.1 節(jié)詳述int &rval1 = 1.01;// 正確int &rval2 = ival;// 錯(cuò)誤,引用必須被初始化,且初始值必須是一個(gè)對(duì)象int &rval3;return 0; }2.16
#include <iostream>int main () {int i = 0, &r1 = i;double d = 0, &r2 = d;// 正確r2 = 3.14159;std::cout << r2 << "\n";// 正確,發(fā)送自動(dòng)類型轉(zhuǎn)換r2 = r1;std::cout << r2 << '\n';// 正確,發(fā)送自動(dòng)類型轉(zhuǎn)換i = r2;std::cout << i << std::endl;// 正確,發(fā)送自動(dòng)類型轉(zhuǎn)換r1 = d;std::cout << r1 << std::endl;return 0; }2.17
#include <iostream>int main () {int i, &ri = i;i = 5;ri = 10;std::cout << i << " " << ri << std::endl;return 0; } // 運(yùn)行結(jié)果 10 10Process finished with exit code 02.18
#include <iostream>int main () {int ival = 23, *ip = &ival;std::cout << "ival = " << ival << " " << "*ip = " << *ip << std::endl;*ip = 24;std::cout << "ival = " << ival << " " << "*ip = " << *ip << std::endl;ival = 25;std::cout << "ival = " << ival << " " << "*ip = " << *ip << std::endl;return 0; } // 運(yùn)行結(jié)果 ival = 23 *ip = 23 ival = 24 *ip = 24 ival = 25 *ip = 25Process finished with exit code 02.19
引用
引用(reference)為對(duì)象起了另外一個(gè)名字,引用類型引用(refers to)另外一種類型。
int ival = 1024; int &refVal = ival; // refVal 指向 ival(是 ival 的另一個(gè)名字) int &refVal2; // 報(bào)錯(cuò):引用必須被初始化 int &refVal4 = 10; // 錯(cuò)誤:引用類型的初始值必須是一個(gè)對(duì)象 // 除特殊情況,其他所有引用的類型都要和與之綁定的對(duì)象嚴(yán)格匹配 double dval = 3.14; int &refVal5 = dval; // 錯(cuò)誤:此處引用類型的初始值必須是 int 型對(duì)象指針
指針(pointer)是"指向(point to)"另外一種類型的復(fù)合類型。與引用類似,指針也實(shí)現(xiàn)了對(duì)其他對(duì)象的間接訪問。然而指針與引用相比又有很多不同點(diǎn)。
指針本身就是一個(gè)對(duì)象,允許對(duì)指針賦值和拷貝,而且在指針的生命周期內(nèi)它可以先后指向幾個(gè)不同的對(duì)象;
指針無須在定義時(shí)賦初值;
因?yàn)橐貌皇菍?duì)象,沒有實(shí)際地址,所以不能定義指向引用的指針;
除特殊情況,其他所有指針的類型都要和它所指向的對(duì)象嚴(yán)格匹配;
空指針的定義方法:
int *p1 = nullptr; // C++11 新標(biāo)準(zhǔn)引人的方法,推薦使用 int *p2 = 0; // 直接將 p2 初始化為字面值常量 0 // 需要首先 #include cstdlib int *p3 = NULL;三種定義空指針的方法等價(jià),推薦使用第一種。
雖然指針無須在定義時(shí)賦初值,但是記得初始化所有指針。如果實(shí)在不清楚指針應(yīng)該指向何處,就把它初始化為 nullptr 或者 0,這樣程序就能檢測(cè)并知道它沒有指向任何具體的對(duì)象了。使用未經(jīng)初始化的指針是引發(fā)運(yùn)行時(shí)錯(cuò)誤的一大原因。
2.20
將 \(i\) 的值修改為 \(i^2\)
#include <iostream>int main () {int i = 42;int *p1 = &i;*p1 = *p1 * *p1;std::cout << "i = " << i << " " << "*p1 = " << *p1;return 0; } // 運(yùn)行結(jié)果 i = 1764 *p1 = 1764 Process finished with exit code 02.21
int i = 0; double *dp = &i; // 錯(cuò)誤,指針的類型都要和它所指向的對(duì)象嚴(yán)格匹配 int *ip = i; // 錯(cuò)誤,忘記取地址符 & int *p = &i; // 正確2.22
if (p) means if the pointer p is not null.
if (*p) means if the object pointed by the pointer is not false (which means the object is not null or zero etc.).
#include <iostream>int main () {int i = 42;int *p = &i;std::cout << "p = " << p << " " << "*p = " << *p << std::endl;if (p)std::cout << "true" << std::endl;elsestd::cout << "false" << std::endl;if (*p)std::cout << "true" << std::endl;elsestd::cout << "false" << std::endl;i = 0;p = &i;std::cout << "p = " << p << " " << "*p = " << *p << std::endl;if (p)std::cout << "true" << std::endl;elsestd::cout << "false" << std::endl;if (*p)std::cout << "true" << std::endl;elsestd::cout << "false" << std::endl;return 0; } // 運(yùn)行結(jié)果 p = 0x7ffee92f3818 *p = 42 true true p = 0x7ffee92f3818 *p = 0 true falseProcess finished with exit code 02.23
No, you can't. Because it would be expensive to maintain meta data about what constitutes a valid pointer and what doesn't, and in C++ you don't pay for what you don't want.
See answer here.
However, a smart pointer can be used to tell if it points to a valid object.
注:此題解答來自
2.24
int main() {int i = 42;void *p = &i; // OK, a `void *` pointer can point to any type//long *lp = &i; // Error, a `long *` pointer can not point to `int *`return 0; }2.25
#include <iostream>int main() {{int* ip, i, &r = i; // `ip` is `int *`, `i` is `int`, `r` is `int &`std::cout << "(a)" << std::endl;std::cout << "ip\t" << typeid(ip).name() << std::endl;std::cout << "i\t" << typeid(i).name() << std::endl;std::cout << "r\t" << typeid(r).name() << std::endl;// Note that `typeid` will lose the `const` qualifier and reference}{int i, *ip = 0; // `i` is `int`, `ip` is `int *`std::cout << "(b)" << std::endl;std::cout << "i\t" << typeid(i).name() << std::endl;std::cout << "ip\t" << typeid(ip).name() << std::endl;}{int* ip, ip2; // `ip` is `int *`, `ip2` is `int`std::cout << "(c)" << std::endl;std::cout << "ip\t" << typeid(ip).name() << std::endl;std::cout << "ip2\t" << typeid(ip2).name() << std::endl;}return 0; } // 運(yùn)行結(jié)果 (a) ip Pi i i r i (b) i i ip Pi (c) ip Pi ip2 iProcess finished with exit code 0注:
Understanding the output of typeid().name()
std::type_info::name
2.26
本題所有語(yǔ)句應(yīng)該被看作是順序執(zhí)行的,即形如:
const int buf; int cnt = 0; const int sz = cnt; ++cnt; ++sz;(a)是非法的,const 對(duì)象一旦創(chuàng)建后其值就不能改變,所以 const 對(duì)象必須初始化。該句應(yīng)修改為 const int buf = 10。
(b)和(c)是合法的。
(d)是非法的,sz 是一個(gè) const 對(duì)象,其值不能被改變,當(dāng)然不能執(zhí)行自增操作。
2.27
(a)是非法的,非常量引用 r 不能引用字面值常量 0。修改方法:
int i = -1, &r = i; // 或 const int i = -1, &r = 0; // 或 const int i = -1, &r = i;(b)是合法的,p2 是一個(gè)常量指針,p2 的值永不改變,即 p2 永遠(yuǎn)指向變量 i2。
(c)是合法的,i 是一個(gè)常量,r 是一個(gè)常量引用,此時(shí) r 可以綁定到字面值常量 0。
(d)是合法的,p3 是一個(gè)常量指針,p3 的值永不改變,即 p3 永遠(yuǎn)指向變量 i2;同時(shí) p3 指向的是常量,即我們不能通過 p3 改變所指對(duì)象的值。
(e)是合法的,p1 指向一個(gè)常量,即我們不能通過 p1 改變所指對(duì)象的值。
(f)是非法的,引用本身不是對(duì)象,因此不能讓引用恒定不變。
(g)是合法的,i2 是一個(gè)常量,r 是一個(gè)常量引用。
2.28
(a)是非法的,cp 是一個(gè)常量指針,因其值(指針存儲(chǔ)的那個(gè)地址)不能被改變,所以必須被初始化。
(b)是非法的,cp2 是一個(gè)常量指針,因其值不能被改變,所以必須被初始化。
(c)是非法的,ic 是一個(gè)常量,因其值不能被改變,所以必須被初始化。
(d)是非法的,p3 是一個(gè)常量指針,因其值不能被改變,所以必須被初始化;同時(shí) p3 所指向的是常量,即我們不能通過 p3 改變所指對(duì)象的值。
(e)是合法的,但是 p 沒有指向任何實(shí)際的對(duì)象。
2.29
(a)是合法的,常量 ic 的值賦給了非常量 i。
(b)是非法的,普通指針 p1 指向了一個(gè)常量,從語(yǔ)法上說,p1 的值可以隨意改變,顯然是不合理的。因?yàn)?p1 改變了,p1 所指對(duì)象因該也同時(shí)改變了,但是 p1 所指對(duì)象是一個(gè)常量,不能被修改。
(c)是非法的,普通指針 p1 指向了一個(gè)常量,錯(cuò)誤情況與上一條類似。
(d)是非法的,p3 是一個(gè)常量指針,不能被賦值。
(e)是非法的,p2 是一個(gè)常量指針,不能被賦值。
(f)是非法的,ic 是一個(gè)常量,不能被賦值。
2.30
頂層 const 表示任意的對(duì)象是常量,而底層 const 與指針和引用等復(fù)合類型的基本類型部分有關(guān)。
解答:v2 和 p3 是頂層 const,分別表示一個(gè)整型常量和一個(gè)整型常量指針;p2 和 r2 是底層 const,分別表示它們所指(所引用)的對(duì)象是常量。
2.31
本題考查頂層 const 和底層 const 對(duì)于拷貝操作的影響。
解答:
在執(zhí)行拷貝操作時(shí),頂層 const 和底層 const 區(qū)別明顯。其中,頂層 const 不受影響,這是因?yàn)榭截惒僮鞑⒉粫?huì)改變被拷貝對(duì)象的值。底層 const 的限制則不容忽視,拷入和拷出的對(duì)象必須具有相同的底層 const 資格,或者兩個(gè)對(duì)象的數(shù)據(jù)類型必須能夠轉(zhuǎn)換。一般說來,非常量可以轉(zhuǎn)換成常量,反之則不行。
r1 = v2; 是合法的,r1 是一個(gè)非常量引用,v2 是一個(gè)常量(頂層 const),把 v2 的值拷貝給 r1 不會(huì)對(duì) v2 有任何影響。
p1 = p2; 是非法的,p1 是普通指針,指向的對(duì)象可以是任意值,p2 是指向常量的指針(底層 const),令 p1 指向 p2 所指的內(nèi)容,有可能錯(cuò)誤地通過 p1 改變 p2 所指常量的值。
p2 = p1; 是合法的,與上一條語(yǔ)句相反,p2 可以指向一個(gè)非常量,只不過我們不會(huì)通過 p2 更改它所指的值。
p1 = p3; 是非法的,p3 包含底層 const 定義(p3 所指的對(duì)象是常量),不能把 p3 的值賦給普通指針。
p2 = p3; 是合法的,p2 和 p3 包含相同的底層 const,p3 的頂層 const 則可以忽略不計(jì)。
2.32
int null = 0, *p = &null; // 或 int null = 0, *p = nullptr;2.33
【出題思路】
本題旨在考察 auto 說明符與復(fù)合類型、常量混合使用時(shí)的各種情形。首先,使用引用其實(shí)是使用引用的對(duì)象,所以當(dāng)引用被用作初始值時(shí),真正參與初始化的其實(shí)是引用對(duì)象的值,編譯器以引用對(duì)象的類型作為 auto 的推斷類型。其次,auto 一般會(huì)忽略掉頂層 const,同時(shí)保留底層 const。
【解答】
前 3 條賦值語(yǔ)句是合法的,原因如下:
r 是 i 的別名,而 i 是一個(gè)整數(shù),所以 a 的類型推斷結(jié)果是一個(gè)整數(shù);ci 是一個(gè)整型常量,在類型推斷時(shí)頂層 const 被忽略掉了,所以 b 是一個(gè)整數(shù);cr 是 ci 的別名,而 ci 是一個(gè)整型常量,所以 c 的類型推斷結(jié)果是一個(gè)整數(shù)。因?yàn)?a、b、c 都是整數(shù),所以為其賦值 42 是合法的。
后 3 條賦值語(yǔ)句是非法的,原因如下:
i 是一個(gè)整數(shù),&i 是 i 的地址,所以 d 的類型推斷結(jié)果是一個(gè)整型指針;ci 是一個(gè)整型常量,&ci 是一個(gè)整型常量的地址,所以 e 的類型推斷結(jié)果是一個(gè)指向整型常量的指針;ci 是一個(gè)整型常量,所以 g 的類型推斷結(jié)果是一個(gè)整型常量引用。因?yàn)?d 和 e 都是指針,所以不能直接用字面值常量為其賦值;g 綁定到了整型常量,所以不能修改它的值。
2.34
【出題思路】
本題旨在考察 auto 說明符與復(fù)合類型、常量混合使用時(shí)的各種情形。
【解答】
基于上一個(gè)練習(xí)中的變量和語(yǔ)句編寫的程序如下所示:
#include <iostream>using namespace std; int main(int argc, char *argv[]) {int i = 0, &r = i;auto a = r;const int ci = i, &cr = ci;auto b = ci;auto c = cr;auto d = &i;auto e = &ci;const auto f = ci;auto &g = ci;cout << a << " " << b << " " << c << " " << d << " " << e << " " << g << std::endl;a = 42;b = 42;c = 42;// d = 42;// e = 42;// g = 42;cout << a << " " << b << " " << c << " " << d << " " << e << " " << g << std::endl;return 0; } // 運(yùn)行結(jié)果 0 0 0 0x7ffee3e7088c 0x7ffee3e70878 0 42 42 42 0x7ffee3e7088c 0x7ffee3e70878 0Process finished with exit code 02.35
【出題思路】
本題旨在考察 auto 說明符與復(fù)合類型、常量混合使用時(shí)的各種情形。
【解答】
由題意可知,i 是一個(gè)整型常量,j 的類型推斷結(jié)果是整數(shù),k 的類型推斷結(jié)果是整型常量,p 的類型推斷結(jié)果指向整型常量的指針,j2 的類型推斷結(jié)果是整型常量,k2 的類型推斷結(jié)果是整型常量的引用。
- i is const int
- j is int
- k is const int &
- p is const int *
- j2 is const int
- k2 is const int &
用于驗(yàn)證的程序是:
#include <iostream> #include <typeinfo>int main() {const int i = 42;auto j = i;const auto &k = i;auto *p = &i;const auto j2 = i, &k2 = i;std::cout << typeid(i).name() << std::endl; // istd::cout << typeid(j).name() << std::endl; // istd::cout << typeid(k).name() << std::endl; // istd::cout << typeid(p).name() << std::endl; // PKistd::cout << typeid(j2).name() << std::endl; // istd::cout << typeid(k2).name() << std::endl; // istd::cout << std::endl;std::cout << std::boolalpha; // 接下來的輸出把 bool 值顯示成 true/falsestd::cout << "i and j have same type? "<< std::is_same<decltype(i), decltype(j)>::value << std::endl;std::cout << "i and k have same type? "<< std::is_same<decltype(i), decltype(k)>::value << std::endl;std::cout << "i and j2 have same type? "<< std::is_same<decltype(i), decltype(j2)>::value << std::endl;std::cout << "j and j2 have same type? "<< std::is_same<decltype(j), decltype(j2)>::value << std::endl;std::cout << "k and k2 have same type? "<< std::is_same<decltype(k), decltype(k2)>::value << std::endl;return 0; } // 運(yùn)行結(jié)果 // print i means int, and PKi means pointer to const int. i i i PKi i ii and j have same type? false i and k have same type? false i and j2 have same type? true j and j2 have same type? false k and k2 have same type? trueProcess finished with exit code 02.36
【出題思路】
本題旨在考察 decltype 與引用的關(guān)系。對(duì)于 decltype 所用的表達(dá)式來說,如果變量名加上一對(duì)括號(hào),得到的類型與不加括號(hào)時(shí)會(huì)有不同。具體來說,如果 decltype 使用的是一個(gè)不加括號(hào)的變量,則得到的結(jié)果就是該變量的類型;如果給變量加上了一層或多層括號(hào),編譯器就會(huì)把它當(dāng)成一個(gè)表達(dá)式,從而推斷得到引用類型。
【解答】
在本題的程序中,初始情況下 a 的值是 3,b 的值是4。decltype(a) c = a; 使用的是一個(gè)不加括號(hào)的變量,因此 c 的類型就是 a 的類型,即該語(yǔ)句等同于 int c = a;,此時(shí) c 是一個(gè)新整型變量,值為 3。decltype((b)) d = a; 使用的是一個(gè)加了括號(hào)的變量,因此 d 的類型是引用,即該語(yǔ)句等同于 int &d = a;,此時(shí) d 是變量 a 的別名。
執(zhí)行 ++c; ++d; 時(shí),變量 c 的值自增為 4,因?yàn)?d 是 a 的別名,所以 d 自增 1 意味著 a 的值變成了 4。當(dāng)程序結(jié)束時(shí),a、b、c、d 的值都是 4。
#include <iostream> #include <typeinfo>int main() {int a = 3, b = 4;decltype(a) c = a;decltype((b)) d = a;++c;++d;std::cout << typeid(c).name() << std::endl; // intstd::cout << typeid(d).name() << std::endl; // int &std::cout << c << std::endl; // 4std::cout << d << std::endl; // 4return 0; }2.37
【出題思路】
decltype 的參數(shù)既可以是普通變量,也可以是一個(gè)表達(dá)式。當(dāng)參數(shù)是普通變量時(shí),推斷出的類型就是該變量的類型;當(dāng)參數(shù)是表達(dá)式時(shí),推斷出的類型是引用。
【解答】
根據(jù) decltype 的上述性質(zhì)可知,c 的類型是 int,值為 3;表達(dá)式 a = b 作為decltype 的參數(shù),編譯器分析表達(dá)式并得到它的類型作為 d 的推斷類型,但是不實(shí)際計(jì)算該表達(dá)式,所以 a 的值不發(fā)生改變,仍然是 3;d 的類型是 int &,d 是 a 的別名,值是 3;b 的值一直沒有發(fā)生改變,為 4。
2.38
【出題思路】
auto 和 decltype 是兩種類型推斷的方式,本題旨在考察二者的區(qū)別和聯(lián)系。
【解答】
auto 和 decltype 的區(qū)別主要有三方面:
一個(gè)用以說明的示例如下所示:
#include <iostream> #include <typeinfo>int main() {int a = 3;auto c1 = a;decltype(a) c2 = a;decltype((a)) c3 = a;const int d = 5;auto f1 = d;decltype(d) f2 = d;std::cout << typeid(c1).name() << std::endl; // intstd::cout << typeid(c2).name() << std::endl; // intstd::cout << typeid(c3).name() << std::endl; // int &std::cout << typeid(f1).name() << std::endl; // intstd::cout << typeid(f2).name() << std::endl; // const intc1++;c2++;c3++;f1++;// f2++; // 錯(cuò)誤:f2 是整型常量,不能執(zhí)行自增操作std::cout << a << " " << c1 << " " << c2 << " " << c3 << " " << f1<< " " << f2 << std::endl;return 0; } // 運(yùn)行結(jié)果 i i i i i 4 4 4 4 6 5Process finished with exit code 0對(duì)于第一組類型推斷來說,a 是一個(gè)非常量整數(shù),c1 的推斷結(jié)果是整數(shù),c2 的推斷結(jié)果也是整數(shù),c3 的推斷結(jié)果由于變量 a 額外加了一對(duì)括號(hào)所以是整數(shù)引用。c1、c2、c3 依次執(zhí)行自增操作,因?yàn)?c3 是變量的 a 的別名,所以 c3 自增等同于 a 自增,最終 a、c1、c2、c3 的值都變?yōu)?4。
對(duì)于第二組類型推斷來說,d 是一個(gè)常量整數(shù),含有頂層 const,使用 auto 推斷類型自動(dòng)忽略掉頂層 const,因此 f1 的推斷結(jié)果是整數(shù);decltype 則保留頂層 const,所以 f2 的推斷結(jié)果是整數(shù)常量。f1 可以正常執(zhí)行自增操作,而常量 f2 的值不能被改變,所以無法自增。
附:
- What is the difference between auto and decltype(auto) when returning from a function?
- decltype vs auto
2.39
#include <iostream>struct Foo { /* 此處為空 */} // 注意:沒有分號(hào) int main() {return 0; } // 編譯無法通過 main.cpp:3:33: error: expected ';' after struct【出題思路】
本題旨在考查類定義的語(yǔ)法規(guī)范,尤其要注意類體結(jié)束之后的分號(hào)必不可少。
【解答】
該程序無法編譯通過,原因是缺少了一個(gè)分號(hào)。因?yàn)轭愺w后面可以緊跟變量名以示對(duì)該類型對(duì)象的定義,所以在類體右側(cè)表示結(jié)束的花括號(hào)之后必須寫一個(gè)分號(hào)。
稍作修改,程序就可以編譯通過了。
#include <iostream>struct Foo { /* 此處為空 */}; int main() {return 0; } Process finished with exit code 02.40
【出題思路】
類的設(shè)計(jì)源于實(shí)際應(yīng)用,設(shè)計(jì) Sales_data 類的關(guān)鍵是理解在銷售過程中應(yīng)該包含哪些數(shù)據(jù)元素,同時(shí)為每個(gè)元素設(shè)定合理的數(shù)據(jù)類型。
【解答】
原書中的程序包含 3 個(gè)數(shù)據(jù)成員,分別是 bookNo(書籍編號(hào))、units_sold(銷售量)、revenue(銷售收入),新設(shè)計(jì)的 Sales_data 類細(xì)化了銷售收入的計(jì)算方式,在保留 bookNo 和 units_sold 的基礎(chǔ)上,新增了 sellingprice(零售價(jià)、原價(jià))、saleprice(實(shí)售價(jià)、折扣價(jià))、discount(折扣),其中 discount = saleprice / sellingprice。
#include <iostream>struct Sales_data {std::string bookNo; // 書籍編號(hào)unsigned units_sold = 0; // 銷售量double sellingprice = 0.0; // 零售價(jià)double saleprice = 0.0; // 實(shí)售價(jià)double discount = 0.0; // 折扣 }; int main() {return 0; }2.41
Sales_data.h
#ifndef SALESDATA_H // we're here only if SALESDATA_H has not yet been defined #define SALESDATA_H// Definition of Sales_data class and related functions goes here #include <iostream> #include <string>// 頭文件不應(yīng)包含 using 聲明 // using namespace std;class Sales_data {// 友元函數(shù)friend std::istream &operator>>(std::istream &, Sales_data &);// 友元函數(shù)friend std::ostream &operator<<(std::ostream &, const Sales_data &);// 友元函數(shù)friend bool operator<(const Sales_data &, const Sales_data &);// 友元函數(shù)friend bool operator==(const Sales_data &, const Sales_data &);public: // 構(gòu)造函數(shù)的 3 種形式Sales_data() = default;Sales_data(const std::string &book) : bookNo(book) {}Sales_data(std::istream &is) { is >> *this; }Sales_data &operator+=(const Sales_data &);std::string isbn() const { return bookNo; }private:std::string bookNo; // 書籍編號(hào),隱式初始化為空串unsigned units_sold = 0; // 銷售量,顯式初始化為 0double sellingprice = 0.0; // 原始價(jià)格,顯式初始化為 0.0double saleprice = 0.0; // 實(shí)售價(jià)格,顯式初始化為 0.0double discount = 0.0; // 折扣,顯式初始化為 0.0 };inline bool compareIsbn(const Sales_data &lhs, const Sales_data &rhs) {return lhs.isbn() == rhs.isbn(); }Sales_data operator+(const Sales_data &, const Sales_data &);inline bool operator==(const Sales_data &lhs, const Sales_data &rhs) {return lhs.units_sold == rhs.units_sold &&lhs.sellingprice == rhs.sellingprice &&lhs.saleprice == rhs.saleprice &&lhs.isbn() == rhs.isbn(); }inline bool operator!=(const Sales_data &lhs, const Sales_data &rhs) {return !(lhs == rhs); // 基于運(yùn)算符 == 給出 != 的定義 }Sales_data &Sales_data::operator+=(const Sales_data &rhs) {units_sold += rhs.units_sold;saleprice = (rhs.saleprice * rhs.units_sold + saleprice * units_sold)/ (rhs.units_sold + units_sold);if (sellingprice != 0)discount = saleprice / sellingprice;return *this; }Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs) {Sales_data ret(lhs); // 把 lhs 的內(nèi)容拷貝到臨時(shí)變量 ret 中,這種做法便于運(yùn)算ret += rhs; // 把 rhs 的內(nèi)容加入其中return ret; // 返回 ret }std::istream &operator>>(std::istream &in, Sales_data &s) {in >> s.bookNo >> s.units_sold >> s.sellingprice >> s.saleprice;if (in && s.sellingprice != 0)s.discount = s.saleprice / s.sellingprice;elses = Sales_data(); // 輸入錯(cuò)誤,重置輸入的數(shù)據(jù)return in; }std::ostream &operator<<(std::ostream &out, const Sales_data &s) {out << s.isbn() << " " << s.units_sold << " "<< s.sellingprice << " " << s.saleprice << " " << s.discount;return out; }#endif重寫練習(xí) 1.20
main.cpp
#include <iostream> #include "Sales_data.h"using namespace std;int main() {Sales_data book;std::cout << "請(qǐng)輸入銷售記錄:" << std::endl;// 輸入格式:ISBN 售出本數(shù) 原始價(jià)格 實(shí)際售價(jià)while (std::cin >> book) {std::cout << "ISBN、售出本數(shù)、原始價(jià)格、實(shí)售價(jià)格、折扣為:" << book << std::endl;}return 0; } // 運(yùn)行結(jié)果 請(qǐng)輸入銷售記錄: 0-201-78345-X 3 20.00 19.00 ISBN、售出本數(shù)、原始價(jià)格、實(shí)售價(jià)格、折扣為:0-201-78345-X 3 20 19 0.95 0-202-78345-X 4 30.00 29.00 ISBN、售出本數(shù)、原始價(jià)格、實(shí)售價(jià)格、折扣為:0-202-78345-X 4 30 29 0.966667重寫練習(xí) 1.21
main.cpp
#include <iostream> #include "Sales_data.h"using namespace std;int main() {Sales_data trans1, trans2;std::cout << "請(qǐng)輸入兩條 ISBN 相同的銷售記錄:" << std::endl;std::cin >> trans1 >> trans2;if (compareIsbn(trans1, trans2))std::cout << "匯總信息:ISBN、銷售本數(shù)、原始價(jià)格、實(shí)售價(jià)格、折扣為:"<< "\n" << trans1 + trans2 << std::endl;elsestd::cout << "兩條銷售記錄的 ISBN 不同" << std::endl;return 0; } // 運(yùn)行結(jié)果 請(qǐng)輸入兩條 ISBN 相同的銷售記錄: 0-201-78345-X 3 20.00 19.00 0-201-78345-X 5 20.00 18.00 匯總信息:ISBN、銷售本數(shù)、原始價(jià)格、實(shí)售價(jià)格、折扣為: 0-201-78345-X 8 20 18.6154 0.930769Process finished with exit code 0重寫練習(xí) 1.22
main.cpp
#include <iostream> #include "Sales_data.h"using namespace std;int main() {Sales_data total, trans;std::cout << "請(qǐng)輸入幾條 ISBN 相同的銷售記錄:" << std::endl;// 每次輸入要求 ISBN 相同,售出本數(shù)隨便,原始價(jià)格相同,實(shí)售價(jià)格隨便if (std::cin >> total) {while (std::cin >> trans)if (compareIsbn(total, trans))total += trans;else { // ISBN 不同std::cout << "當(dāng)前書籍 ISBN 不同" << std::endl;break;}std::cout << "有效匯總信息:ISBN、售出本數(shù)、原始價(jià)格、實(shí)售價(jià)格、折扣為:"<< "\n" << total << std::endl;} else {std::cout << "沒有數(shù)據(jù)" << std::endl;return -1;}return 0; } // 運(yùn)行結(jié)果(實(shí)售價(jià)格為均值,具體可查看 Sales_item.h 運(yùn)算符 += 重載的實(shí)現(xiàn) 請(qǐng)輸入幾條 ISBN 相同的銷售記錄: 0-201-78345-X 3 20.00 19.00 0-201-78345-X 5 20.00 29.00 0-202-78345-Y 2 200.00 199.00 當(dāng)前書籍 ISBN 不同 有效匯總信息:ISBN、售出本數(shù)、原始價(jià)格、實(shí)售價(jià)格、折扣為: 0-201-78345-X 8 20 22.8462 1.14231Process finished with exit code 0重寫練習(xí) 1.23
#include <iostream> #include "Sales_data.h"using namespace std;int main() {Sales_data trans1, trans2;int num = 1; // 記錄當(dāng)前書籍的銷售記錄總數(shù)std::cout << "請(qǐng)輸入若干銷售記錄:" << std::endl;if (std::cin >> trans1) {while (std::cin >> trans2) {if (compareIsbn(trans1, trans2)) // ISBN 相同num++;else { // ISBN 不同std::cout << trans1.isbn() << " 共有 "<< num << " 條銷售記錄" << std::endl;trans1 = trans2;num = 1;}std::cout << trans1.isbn() << " 共有 "<< num << " 條銷售記錄" << std::endl;}} else {std::cout << "沒有數(shù)據(jù)" << std::endl;return -1;}return 0; } // 運(yùn)行結(jié)果 請(qǐng)輸入若干銷售記錄: 0-201-78345-X 3 20.00 19.00 0-201-78345-X 2 20.00 18.00 0-201-78345-X 共有 2 條銷售記錄 0-201-78345-X 5 20.00 19.00 0-201-78345-X 共有 3 條銷售記錄 0-202-78345-Y 3 220.00 129.00 0-201-78345-X 共有 3 條銷售記錄 0-202-78345-Y 共有 1 條銷售記錄 0-201-78345-X 7 20.00 19.99 0-202-78345-Y 共有 1 條銷售記錄 0-201-78345-X 共有 1 條銷售記錄注:1.6 節(jié)練習(xí)與重寫練習(xí) 1.23 相同
2.42
2.41 已實(shí)現(xiàn)該題功能
轉(zhuǎn)載于:https://www.cnblogs.com/kafffka/p/10963854.html
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
- 上一篇: 为什么JS是单线程?JS中的Event
- 下一篇: 网路编程 客户端