C++Primer第五版 第六章 课后习题答案
第六章
6.6
形參、局部變量和局部靜態變量
- 形參和定義在函數體內部的變量統稱為局部變量,它們僅在函數的作用域內可見
- 函數體內的局部變量可以分為普通局部變量和靜態局部變量
- 形參:在函數開始時便為其申請內存空間,調用時用實參進行初始化。形參是只存在于塊執行期間的對象
- 普通局部變量在所載塊結束后就失效了
- 靜態局部變量對應的對象稱為局部靜態對象,它的生命周期從定義語句開始一直到程序結束
6.8
在頭文件內進行函數聲明:和變量類似,在頭文件聲明然后在源文件定義。預處理器變量的名字全部大寫,可以避免與程序中別的實體發生名字沖突。
#ifndef CHAPTER_H_INCLUDED #define CHAPTER_H_INCLUDEDint fact(int); double myABS(double);#endif以下引用自:#pragma指令與#ifndef指令區別
在C/C++中,在使用預編譯指令#include的時候,為了防止重復引用造成二義性,通常有兩種方式——
- 第一種是#ifndef指令防止代碼塊重復引用,比如說
- 第二種就是#pragma once指令,在想要保護的文件開頭寫入
#pragma once
- #ifndef方式是C/C++語言的標準支持,也是比較常用的方式,#ifndef的方式依賴于自定義的宏名(例中的_CODE_BLOCK)不能沖突,它不光可以保證同一份文件不會被包含兩次,也能夠保證不同文件完全相同的內容不會被包含兩次。
但,同樣的,如果自定義的宏名不小心“重名”了,兩份不同的文件使用同一個宏名進行#ifndef,那么會導致編譯器找不到聲明的情況(被編譯器判定為重定義而屏蔽了)
此外,由于編譯器每次都需要打開頭文件才能判定是否有重復定義,因此在編譯大型項目時,#ifndef會使得編譯時間相對較長,因此一些編譯器逐漸開始支持#pragma once的方式(Visual Studio 2017新建頭文件會自帶#pragma once指令) - #pragma once一般由編譯器提供保證:同一個文件不會被包含多次。這里所說的”同一個文件”是指物理上的一個文件,而不是指內容相同的兩個文件。無法對一個頭文件中的一段代碼作#pragma once聲明,而只能針對文件。
此方式不會出現宏名碰撞引發的奇怪問題,大型項目的編譯速度也因此提供了一些。缺點是如果某個頭文件有多份拷貝,此方法不能保證它們不被重復包含。在C/C++中,#pragma once是一個非標準但是被廣泛支持的方式。
- 二者的區別:
#pragma once方式產生于#ifndef之后。#ifndef方式受C/C++語言標準的支持,不受編譯器的任何限制;而#pragma once方式有些編譯器不支持(較老編譯器不支持,如GCC 3.4版本之前不支持#pragmaonce),兼容性不夠好。#ifndef可以針對一個文件中的部分代碼,而#pragma once只能針對整個文件
6.9(分離式編譯)
- 分離式編譯允許我們把程序分割到幾個文件中,每個文件獨立編譯
- 舉例:(C++Primer第五版 第6.1.3節)
fact函數定義在fact.cc中;fact函數聲明位于Chapter.h中;fact函數在factMain.cc文件中的main函數中被調用。
這一過程通常會產生一個后綴名為.obj(Windows)或者.o(UNIX)的文件,含義是該文件包含object code。
編譯器會把對象文件鏈接在一起形成可執行文件。 - 分離式編譯時具體代碼
fact.cc
factMain.cc
#include "Chapter6.h" #include <iostream> using namespace std; //調用函數fact int main() {int num;cout << "請輸入一個數" << endl;cin >> num;cout << num << "的階乘是:" << fact(num) << endl;return 0; }6.12(傳值和傳引用)
使用引用可以直接操作所引用的對象,而且可以避免拷貝。
void f(T): 形參采用傳值的方式,實參被拷貝給形參
void f(T&):形參采用傳引用的方式,實參是形參的別名
6.14(引用傳遞相對于值傳遞的優勢)
6.23
三個版本print函數:依次調用,輸出int i = 0; 和 int j[2] = {0, 1};的 i 和 j
- 不控制指針的邊界(參數是常量整型指針)
- 調用者指定數組的維度(兩個參數,分別是常量整型指針和數組的容量)
- 使用C++11新規定的begin和end函數限定數組邊界(兩個參數,分別是數組的首尾邊界)
- 調用如下
6.24(不指定形參數組維度)
void print(const int ia[10]) { //循環輸出數組的值}形參ia的維度10此時只是我們期望的,但是實際上不一定,而且真實維度如果不是10我們依然可以正常調用print函數
修改后應該是:void print(const int ia[], const int sz) { //循環輸出數組的值}
6.25(main處理命令行選項 6.2.5節)
帶參數的main與控制臺命令行輸入
6.27(含有可變形參的函數 6.2.6節)
一個可以計算列表中所有元素的和的函數:
6.31(引用何時有效)
引用所引的是函數開始之前就已經存在的對象,則返回該引用是有效的
引用的是函數的局部變量則隨著函數的結束,局部變量失效,此時返回的引用無效
不希望改變返回的對象時,返回對常量的引用
6.32
int &get(int *array, int index) {return array[index];}
注意return處不要加&,否則會變成返回 array[ index ] 的地址
6.36(返回數組的引用的函數)
string (&func( )) [10];
&func( )表示函數的返回結果是一個引用
(&func( )) [10]表示引用的對象是一個維度為10的數組
string (&func( )) [10]表示數組的元素是string對象
6.37
直接編寫返回數組引用的函數比較繁瑣,使用類型別名,尾置返回類型和decltype關鍵字都可以簡化
- 使用類型別名
- 使用尾置返回類型
- 使用delctype關鍵字
返回數組指針
- 使用類型別名
arrT是含有10個整數的數組的別名,func返回一個指向含有10個整數的數組的指針
- 聲明一個返回數組指針的函數(不使用類型別名)
定義返回數組指針的函數,數組的維度必須在形參列表的后面
例子:int (*func( int i )) [10] ;的逐層理解
func(int i) 調用func函數需要一個int類型的實參
(*func( int i )) 可以對函數的調用結果解引用,所以必須加括號,否則返回指針的數組
(*func( int i )) [10] 解引用func的調用得到一個大小為10的數組
int (*func( int i )) [10] 表示數組中的元素是int類型 - 使用尾置返回類型
尾置返回類型跟在形參列表的后面并且以一個 -> 符號開頭,函數真正返回類型在形參列表之后,原本防止返回類型的地方放置auto
注意解引用運算符加了括號
- 使用decltype
在已知函數返回的指針指向那個數組的情況下可以用decltype關鍵字聲明返回類型
arrPtr使用關鍵字decltype表示它的返回類型是一個指針,且指針指向的對象和odd類型一致。decltype并不負責把數組類型轉換為對應的指針,decltype的結果是一個數組,必須在函數聲明的時候加一個*符號表示arrPtr返回指針。
6.43(內聯函數和constexpr函數)
內聯函數和constexpr函數通常定義在頭文件內,以保證多個定義完全一致,同時內聯函數的定義應該對編譯器來說是可見的,編譯器才能在調用點內聯展開函數的的代碼
- 內聯函數,在函數的返回類型前面加 inline ,但是該請求編譯器可以忽略
- constexpr函數,能用于常量表達式的函數,constexpr函數額外規定:函數的返回類型和所有形參的類型都是字面值類型;函數體有且只有一條return語句。
- constexpr函數不一定返回常量表達式,即允許constexpr函數的返回值不是常量
const xize_t scale( size_t cnt) { return new_sz() * cnt; }
scale(2)返回常量表達式,int i = 2, scale(i)返回的不是常量表達式
6.47(調試器 6.5.3節)
assert 預處理宏
assret(espr),表達式為假,assert輸出信息并終止程序
NDEBUG
如果定義了NDEBUG,則assert什么也不做
使用 #define 語句定義NDEBUG,可以關閉調試狀態
- 可以使用NDEBUG編寫自己的條件調試代碼,NDEBUG未定義就會執行 #ifndef
和#endif之間的代碼,NDEBUG已定義這些代碼會被忽略:
此處如果打開調試器,就會執行中間的內容,關閉調試器則不會
6.49(候選函數和可行函數 6.6節)
- 函數匹配第一步:選出調用對應的重載函數集,集合內的就是候選函數
- 函數匹配第二步:候選函數中選出可以被這組實參調用的函數,即可行函數(形參和實參數量相等,實參和形參類型相同或者可以轉化)
6.52(實參類型轉換 6.6.1)
實參類型到形參類型轉換的等級排序:
6.54(函數指針 6.7節)
- 函數的類型由返回類型和形參類型共同決定,例如指向函數類型bool (const string &, const string &)的函數指針:bool (*pf)(const string &, const string &):
pf 前面由*表示 pf 是一個指針,右側形參列表表示 pf 指向的是函數,左側的是函數的返回類型
如果沒有括號變成bool *pf(const string &, const string &),pf 就是一個返回bool指針的函數 - 如果把函數名作為一個值使用,它就會自動地轉換成指針
- 直接用指針調用函數,不需要提前解引用,以下三個調用等價
- 可以為函數指針賦值nullptr或0,表示不指向任何一個函數:pf = nullptr
- 重載函數指針舉例
- 函數指針可以作為形參使用,形參雖然看起來是函數類型但是實際上被當成指針使用
簡化函數指針(類型別名 或者 decltype)
重點注意:
①函數類型,但是在作為形參的化會自動轉換為指針
②decltype返回的函數類型不會自動轉化成指針類型,需要在結果上加上*
③和形參不同,函數返回類型不會自動轉換成指針
- 此處的func1只是函數類型,但是在作為形參的化會自動轉換為指針
- decltype返回的函數類型不會自動轉化成指針類型,需要在結果上加上*
- 重新聲明f:把void f(bool pf(const string &, const string &));改為如下
返回指向函數的指針
- 聲明返回函數指針的函數最簡單的辦法是使用類型別名
- 和形參不同,返回類型不會自動轉換成指針,我們必須顯示第將返回類型指定為指針
- 直接聲明,不用類型別名
- 尾置返回類型
- decltype:牢記decltype返回函數類型而不是指針類型
一個元素是指向bool (const string &, const string &)的指針的vector
總結
以上是生活随笔為你收集整理的C++Primer第五版 第六章 课后习题答案的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Unity漫延的实现思路(流体漫延, 火
- 下一篇: Java中的IO整理