C++ Primer 筆記+習題解答(六)

今天是第六篇筆記,主要內容是函數。現在的節奏基本就是一天讀書一天筆記總結。

若有錯誤 請指正 謝謝

0.引言:

 1.函數:

是一個命名的代碼快,通過調用函數可以執行相應的代碼。函數通常會返回一個結果。

 2.構成:

返回類型 ,函數名 ,形參列表 ,函數體。

 3.調用:

通過調用運算符(())也就是一對圓括號。調用運算符作用於一個表達式,表達式通常是函數名或者指向函數的指針。圓括號內放實參表,用於初始化對應的形參。看清楚,是初始化,就是我們前面所說的變量初始化,所以理解這個後面就很省力了。

 4.流程:

1.用實參初始化形參列表。2.將控制權從主調函數移交給被調函數,也就是說主調函數的執行被中斷,被掉函數開始執行,當被調函數執行完畢,控制權移交回主調函數。

 5.return 語句:

若有值的話,返回值並且結束被調用函數的執行。沒有值直接結束函數的執行。

 6.形參實參:

存在一一對應的關係。形參一定要被初始化,但是不規定初始化的順序,也就是前面所說的求值順序。

 7.形參列表:

可以爲空,但是不可以省略。空形參表有兩種形式:

<span style="color:#330099;">void fun();
void fun(void);</span>

 8.返回類型:

1.void類型,比較特殊的一種。2.返回類型不能是數組,函數。但是可以返回指向數組的指針和指向函數的指針。

 9.局部對象:

名字有作用域,對象有生命週期。

名字作用域是程序文本的一部分,名字在作用域中可見。

對象生命週期死程序執行過程中對象存在的一段時間。

函數體是一個語句塊,同時存在一個作用域。在形參中或者域函數體內部定義的變量統稱爲局部變量(local variable)。他們僅在作用域中可見,同時還會隱藏域外的同名對象。通常來說聲明週期是從定義處到塊末尾,也就是說對象的聲明週期取決於它的定義方式。

自動對象:控制路徑經過此變量定義的時候創建,定義塊的末尾進行銷燬,我們把只存在於快執行期間的變量稱爲自動對象。若定義的變量有初始值,那麼用此值進行初始化,否則執行默認初始化,也就是說可能是垃圾值。

局部靜態對象(local static variable):生命週期貫穿於函數調用以及調用結束後的時間。也就是說函數執行完畢後對此值是不影響的。靜態局部對象是默認初始化爲0的,也就是執行值初始化。

 10.函數聲明:

函數可以多次聲明,但只能定義一次。類似於普通變量,名字要在使用之前聲明。通常把聲明放在頭文件中。因爲聲明中並不用到形參,所以形參是可以省略的。

函數聲明三要素:返回類型,函數名,形參表。描述了函數接口,也稱爲函數原型(function prototype)

 11.分離式編譯:

可以單獨編譯文件。當修改一個文件的時候,只要單獨編譯修改過的文件就可以,不需要動全身。

1.參數傳遞:

  0.引子:

函數每次調用的時候都會創建它的形參,並用傳入它的實參對它進行初始化,初始化的流程同變量的初始化完全一致。

一般有兩種參數交互方式:pass by value and pass by reference .也就是常見的傳值和傳引用。

 1.傳值:

司空見慣的方式,不贅述。這個地方我們把傳地址的方式也歸結到穿值的方式。其實這樣的做法纔是透徹理解的行爲。我一開始是區分的,後來有一天我遇到了二叉樹的創建問題,結合今天的理解,纔算是明悟。

指針形參:在函數體內對指針自身進行修改是不影響源指針的,這個地方雖然是指針,當時當你對指針進行修改的時候,就是傳值傳遞,傳的是一份拷貝。只有當你在對指針進行解引用操作,修改纔會涉及變量自身。

示例:

<span style="font-size:18px;color:#330099;">void func(int *p){
  p=nullptr;
  *p=0;
}
int i=10,*ptr=&i;
func(ptr)</span>
這個地方我就是想強調對ptr的操作就是傳值的方式,也就是說你在體內無論如何修改p,都不會影響ptr,因爲這就是指針間的傳值拷貝。只有指針涉及到解引用的操作的時候纔會影響i.如果你還沒看懂我的意思,那麼你可以看一下爲什麼二叉樹創建會用二級指針或者是指針的引用。因爲在二叉樹的創建裏涉及到指針的自身操作,如果是普通的傳值,在裏面的操作是不會影響實際參數的。所以你的創建肯定是失敗的。這就是爲什麼把這個操作歸結到傳值的原因。

  2.傳引用:

也是司空見慣的方式。剛剛上面我提到指針的引用,應該是歸結到引用這裏的。當引用是形參的時候,這個時候和形參進行交互實際上就是別名的操作了,對別名的操作統統會在實參上得到反饋。而最大的好處就是提高效率,因爲避免了拷貝的行爲。

 3.返回多個值:

不要被這句話誤導了,函數還是隻有一個返回值。這個地方的返回多個值的意思是把函數體內的信息帶出來,因爲函數體內的東西是有聲明週期和作用域的。

如何把函數內的信息帶出來呢?

兩個方式:1.自己定義一個數據類型,比如類,你可以返回一個類。2.用引用傳遞一個額外參數,通過這個行爲,我們都可以讓void函數返回一個值,只是通過的渠道不通而已。

 4.const 形參和實參:

這個地方有必要理下頂層const和底層const。

當用實參初始化const形參的時候,其中的頂層const是可以被忽略的。也就是說我們可以用常量或者非常量初始形參。

示例:

<span style="font-size:18px;color:#330099;">int var=10;
const int cvar=var;//頂層const。
const int ccvar=10;</span>
這個地方只是舉了初始的例子,函數內是一樣的。我們可以看到,雖然類型不同,但是初始化完全沒我問題。

甚至忽略頂層const還會導致一個重載函數的錯誤。

示例:

<span style="font-size:18px;color:#330099;">void fun(int i);
void fun(const int i);
//這是兩個函數嘛 ?</span>
實際上編譯器會認爲這是一個函數,因爲他們兩的形參是可以互通的,而重載的區分是通過形參的數量和類型區分的。

 5.指針或引用形參與const:

我們可以使用非常量初始一個底層const對象,但是反過來是不可以接受的。同時普通引用必須用同類型進行初始化。

示例:

<span style="font-size:18px;color:#330099;">int i=10;
const int &ref=i;
const int *p=&i;
int *ptr=p;//錯。
int &re=10;//錯誤。
int &r=ref;//錯誤。 
</span>
同樣的,在函數裏初始化是同樣的要求。

 6.數組形參:

數組的兩個特殊性質:1.不能拷貝。2.使用其時會轉換爲指針。

不能拷貝要求我們不能進行傳值調用,轉換告訴我們相當於傳遞指針。

考慮下面三個聲明:

<span style="font-size:18px;color:#330099;">void print(const int*);
void print(const int[]);
void print(const int[10]);</span>
算是重載嘛?不算。因爲他們都是一個類型:const int*.

因爲數組以指針傳遞的方式傳遞給函數,但是函數是當成指針理解,並不知道數組的具體大小,爲了防止越界行爲發生,我們提供了三種改進措施。

  1.顯式的提供一個委外參數表示數組大小。

示例:

<span style="font-size:18px;color:#330099;">int arr[10]={0};
void print(const int[],int size);</span>
最簡單的方式,我們直接提供數組大小的信息就可以了。

  2.使用標記指定的數組長度,要求數組本身擁有結束字符。最常見的就是c-style 字符串了。自動空字符'\o'。

示例:

<span style="font-size:18px;color:#330099;">char cstr[]="C++";
void print(const char* p){
  while(*p)
    cout<<*p++<<" ";
}
#include <iostream>
using namespace std;
void print(const char *p){
    while (*p){
        cout << *p++ << " ";
    }
}
int main(){
    char cstr[] = { 'c', '+','+','\0' };
    char str[]={'c','+','+'};//試試輸出這個。
    char c="C++";//試試這個。
    print(cstr);
    system("pause");
    return 0;
}
</span>

  3.借用標準庫函數:

採用begin 和 end作爲形參:

<span style="font-size:18px;color:#330099;">void print(const int *beg,const int *end);
int arr[10]={0};
調用:
print(begin(arr),end(arr));</span>
看過前面章節的人肯定知道這兩個函數的神奇。

 7.數組形參和const:

提到const,肯定是隻讀相關的。當我們不需要對數組中的元素進行寫值的時候,定義成執行常量的指針最好。

 8.數組引用形參:

看個例子就明白了.

示例:

<span style="font-size:18px;color:#330099;">void print(int (&ref)[10]);</span>
請務必在引用符號前寫上括號。我想不用區分數組的引用和引用的數組,後者根本是一個子虛烏有的東西。

 9.傳遞多維數組:

前面也提過,比存在多維數組這個東西,其實就是數組的嵌套。在一維數組中,我們傳遞進去的是數組名,也可以理解成指向首元素的指針。當傳遞多維數組的時候,我們可以類比下。

多維數組中的每個元素自身都是一個數組,如果你要傳遞一個東西指向數組的第一個元素。數組第一個元素還是數組,所以我們傳遞一個指向數組的指針就可以了。

<span style="font-size:18px;color:#330099;">void print(int (*p)[10]);
void print(int [][10]);//簡單粗暴的寫法了。 
</span>
說是說傳遞二維數組,傳遞進去的是指向數組的指針。

 10.含有可變形參的函數:

爲了提供了不同數量的形參,C++11提供了兩種方法:

1.如果未知數量的實參類型相同,可以使用一個標準庫類型當作形參。

2.若實參類型不同,那麼可以需要編寫可變參數模版,暫時不表。

C++還提供了一種特殊的形參類型,省略符形參。我們可以用它傳遞可以變數量的實參,但是它一般用於C函數的接口函數。

initializer_list形參:這個一個標準庫類型,具體使用方法可以參考向量。肯定要頭文件,附加類型信息等。此標準庫類型是用於表示某種特殊類型的數組。函數此形參當然也可以同時含有其他形參。

省略符形參:使用了名爲varargs的c標準庫功能。一般僅僅用於c車c++ 共有的類型,當使用類類型的時候可能會產生錯誤。

省略符形參只能出現在形參列表的最後一個位置,所以語法格式越就兩種了.

示例:

<span style="font-size:18px;color:#330099;">1.void func(parameter-lisnt,...);
2.void func(...);
1中的,是可選的。
</span>
省略符部分是不進行類型檢查的。

2.返回類型和return 語句:

0.格式:

<span style="color:#330099;">1.return ;
2.return expression;
</span>

1.無返回值

void不要求非要寫上return語句,因爲編譯器爲隱式的執行。但是一條是肯定的,此種return語句只能用於void函數中。

當然void也可以用第二種格式,只是比較少見而已,而且用發有一些要求。要求就是後面的表達式必須是返回void函數。

2.有返回值:

提供函數的返回結果,只要寫了就必須加上返回值。其中返回和返回類型必須是相關類型。

3.值的返回:

0.引子:

還是類似於變量初始化,返回的值用於初始化調用點處的一個臨時量,該臨時量就是函數調用的結果。

大忌:不要返回局部對象的引用或者指針。

小菜:返回類類型和調用運算符。意思就是使用函數的返回結果再次調用函數。一般要求函數的返回類型是引用或者相關指針類型。

1.引用返回左值:

調用返回引用的函數就可以得到左值,左值就可以往裏面寫東西的嘛。

示範:

<span style="font-size:18px;color:#330099;">int& func(const int &i){
  return i;
}
int k=func(3);
func(3)=10;//這個就是返回左值的例子。
</span>

2.返回列表初始值:

一看就知道和C++11相關。C++11規定,函數可以返回花括號包圍的初始值列表。若是內置類型,列表中最多有一個值。但是自定義類型,那就看具體定義。比如vector,可變長的。

3.main函數的返回值:

允許main函數無返回值,因爲隱式的會執行。一般來說返回0表示執行成功,非0值的具體含義由具體機器決定。

我們可以使用頭文件cstdlid中定義的兩個預處理變量。

示例:

<span style="font-size:18px;color:#330099;">return EXIT_FAILURE;
return EXIT_SUCCESS;
//主要包含頭文件。</span>

4.遞歸插曲:

1.自己調用自己。2.main函數不能遞歸。3.遞歸要有個出口,不然會演變爲遞歸循環。

5.返回數組的指針:

我們是知道的,數組是不能拷貝的,所以無法返回值。但是可以返回數組的指針或者引用。爲了簡化書寫,我們通常會使用別名進行簡化

示例:

<span style="font-size:18px;color:#330099;">typedef int arr[10];
using arr=int[10];
//兩種方法都可以起別名。
arr* func(int i);</span>
不簡化的書寫格式:
<span style="font-size:18px;color:#330099;">type (*func(int i)) [10];
int (*func(int i))[10];
</span>
比較麻煩的寫法,不好記憶。

我們可以使用位置返回類型進行優化。顧名思義,尾巴放上返回類型,着實新奇。

<span style="font-size:18px;color:#330099;">auto func(int i) ->int (*)[10];</span>
原來寫類型的地方換成了auto,在後面組合箭頭運算符寫上了真正的類型。C++11 規定,任何函數都可以使用尾置返回類型,但是一般的也沒必要使用,直接一步到位就可以了,複雜的可以寫成這個樣子。

同樣的可以用一下decltype 進行優化。

<span style="font-size:18px;color:#330099;">int arr[10]={0};
decltype(arr)* func(int i);</span>
這個地方就強調一點decltype 並不負責把數組類型轉換成對應的指針類型,所以*號是自己加上的,不然就是返回數組類型了。

4.函數的重載:

0.引子:

1.同一作用域內,同名函數,參數列表不同。2.main函數不能重載。

2.const 形參中的const僅僅是修飾嘛?不是,它還會起到類型限定的作用,不是說僅僅確保參數在函數內不會被修改。

3.重載的區分:形參類型和數量,返回值類型不算。

4.上面介紹過了,有頂層const和普通形參不算重載。但是對於底層const就算是重載了,具體原因上講過了。

5.const_cast 重載,其中的轉換是雙向的。

1.調用重載函數:

函數匹配→最近匹配。不匹配或者多個匹配報錯,比如二義性調用。

2.重載和作用域:

前面已經講過,同一作用域內纔算是重載。

將函數的聲明寫在函數體內是不明智的。

會產生同名隱藏的行爲。

名字查找發生在類型檢查之前。

5.特殊用途的語言特性:

1.默認實參:

多次調用卻不需要改變的值一般設置爲默認實參。調用含有默認實參的函數可以省略參數。

一旦一個形參有默認實參,那麼其後的所有形參都必須含有默認值。

合理的設置默認實參的順序是重點,通常將不經常改變的量往後放並設置默認形參。

默認實參的聲明:在給定的作用域,一個形參只能被賦予一次實參,但是可以給沒用默認實參的形參賦予默認形參。

默認實參的初始值:除了局部變量不能做默認實參。

2.內聯函數:

1.函數調用的開銷:1.調用前保存寄存器,並在返回的時候恢復。2.可能需要拷貝實參。3.程序間的跳轉。

 2.使用內聯函數可以避免開銷,因爲直接是替換展開。一般用於規模較小,流程直接,頻繁調用的函數。

3.遞歸函數一般不內聯。內聯向編譯器發出的請求,編譯器可以忽略。

3.constexpr函數:

能用於常量表達式的函數.

什麼是常量表達式?編譯時就可以計算出結果並且值不會改變。

此函數定義和普通函數類似,但是遵循一些規定:所有的形參和返回值必須是字面值類型,主要是字面值類型而不是字面值,函數只有一條return語句並且必須有。

因爲在執行初始化的時候,編譯器把函數調用轉換爲值替換,所以是隱式內聯。

<span style="font-size:18px;color:#330099;">constexpr int func(int i){
   return 42*i
}
func(2)可以用作常量。
func(a)不可以用作常量。這個地方就可以清楚什麼叫字面值類型了吧。
</span>
constexpr函數不一定返回常量類型。

內聯函數和constexpr函數可以多次定義,考慮到特殊性,一般把他們定義在頭文件中,注意是定義,不是聲明。

4.調試幫助:

1.assert(expr) 對expr進行求值,若爲0,則assert執行並終止程序,否則不執行。

2.asset定義在頭文件中cassert中。不可以定義同名對象或函數等其他實體。

3.NDEBUG:assert 的行爲取決於NDEBUG的狀態,定義了它,那麼assert 不執行,否則assert執行運行時檢測。

默認是不定義NDEBUG。

4.我瞭解的不多,用到的時候慢慢學習吧。

6.函數匹配:

候選函數→可行函數→最佳匹配。

7.函數指針:

1.函數指針:

是指向函數而非對象。函數的類似由返回類型和形參列表構成,與函數名無關。

我們不可以返回函數類型,但是可以返回執行函數的指針。

<span style="font-size:18px;color:#330099;">void print(int i);
void (*pf)(int i);
pf=print;
pf=&print;
調用時都可以接受。
</span>
調用時無需解引用。指向不同類型的函數指針不存在轉換規則。

2.重載和函數指針:

<span style="font-size:18px;">void print(int i);
void print(double i);
void (*pf)(int i)=print;//天經地義
void (*ppf)(int *)=print;//不匹配。</span>
函數指針形參:函數自己不可以當做形參,但是函數的指針就可以。用在形參時候可以下簡化書寫。常見的就是別名等了。

當然C++11還有別的工具。用decltype推斷出來的類型需要自己加上*號。

返回指向函數的指針:可以用位置類型或者別名優化下格式書寫。

tips:decltype用於函數指針類型時,它返回的是函數類型而非指針類型記得自己顯式加上*號。

8.習題解答:

6.1

<span style="font-size:18px;">區別:實參是用來初始化形參的。
形參是調用函數時我們創建的。
實參是我們調用函數時傳遞進去初始化對應形參。</span>
6.2
<span style="font-size:18px;">a)
返回值類型不匹配。
修改:return atoi(s.c_str());
b)
缺少返回類型。
修改: int f2(int i)
c)
形參列表同名。
修改:int calc(int v1,int v2);
d)
缺少花括號。
修改:
double square(double x){
 return x*x;
}
</span>
6.3
<span style="font-size:18px;">#include <string>
#include<iostream>
using namespace std;
long long fact(const int &var,long long &result){
	if (var == 0)
		result = 1;
	else{
		result = fact(var - 1,result)*var;
	}
	return result;
}
int main(){
	long long sum = 0;
	cout << fact(5,sum) << endl;
	system("pause");
	return 0;
}</span>
6.4
<span style="font-size:18px;">#include <string>
#include<iostream>
using namespace std;
long long fact(const int &var,long long &result){
	if (var == 0)
		result = 1;
	else{
		result = fact(var - 1,result)*var;
	}
	return result;
}
int main(){
	long long sum = 0;
	int val = 0;
	cout << "Enter a number ";
	cin >> val;
	cout <<val<<"! is "<< fact(val,sum) << endl;
	system("pause");
	return 0;
}</span>
6.5
<span style="font-size:18px;">#include <string>
#include<iostream>
using namespace std;
/*int abs_func(int var){
	return abs(var);
}*/
template<class T>
T abs_func1(T var){
	if (var < 0)
		return var *= -1;
	else
		return var;
}
int main(){
	long long sum = 0;
	int val = 0;
	cout<<abs_func1(-3.1);
	cout << abs_func1<double>(-3);
	cout << abs_func1<int>(-10.2);
	system("pause");
	return 0;
}</span>
6.6
<span style="font-size:18px;">局部變量包含後兩者。
形參:在定義函數時定義的變量。
局部靜態變量:在函數體內部創建的靜態變量,在函數體可見,但是生命週期延遲到程序結束。
int func(int var){  //var是形參、
    static int i=var;//i是靜態局部變量、
    ++   i;
    return i
} 
</span>
6.7
<span style="font-size:18px;">#include <string>
#include<iostream>
using namespace std;
int func(){  
	static int i;
	return i++;
}
int main(){	
	for (int i = 0; i != 10; i++){
		cout << func() << " ";
	}
	system("pause");
	return 0;
}</span>
6.8
<span style="font-size:18px;">#ifndef CHAPTER_H
#define CHAPTER_H
#include <iostream>
int fact(int val);
#endif</span>
6.9
<span style="font-size:18px;">//chapter.h
#ifndef CHAPTER_H
#define CHAPTER_H
#include <iostream>
int fact(int val);
#endif
//fact.cpp
#include "chapter.h"
int fact(int val){
    return val+1;
}
//main.cpp
#include "chapter.h"
#include<iostream>
using namespace std;
int main(){    
    for (int i = 0; i != 10; i++){
        cout << fact(i) << " ";
    }
    system("pause");
    return 0;
}
</span>
6.10
<span style="font-size:18px;">#include<iostream>
using namespace std;
void Swap_int(int *p1, int *p2){
	int temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}
int main(){	
	int var1, var2;
	cout << "Enter two numbers ";
	cin >> var1 >> var2;
	cout << "Before callin ,two values are " << var1 << " " << var2 << endl;
	Swap_int(&var1, &var2);
	cout << "After swaping , two value are " << var1 << " " << var2 << endl;

	system("pause");
	return 0;
}</span>

6.11

<span style="font-size:18px;">#include <iostream>
using namespace std;
void reset(int &i){
	i = 0;
}
int main(){
	int var = 0;
	cout << "Enter a number ";
	cin >> var;
	cout << "reset the number to 0 " << endl;
	reset(var);
	cout << "Now the value is " << var << endl;
	system("pause");
	return 0;
}</span>
6.12
<span style="font-size:18px;">#include <iostream>
using namespace std;
void Swap_int(int& var1, int& var2){
	int temp = var1;
	var1 = var2;
	var2 = temp;
}
int main(){
	int va1, va2;
	cout << "Enter two numbers ";
	cin >> va1 >> va2;
	cout << "Before swaping , two numers are " <<" va1 = "<<va1<< " " <<" va2 = "<<va2 << endl; 
	Swap_int(va1, va2);
	cout << "After swaping , two numers are " << " va1 = " << va1 << " " << " va2 = " << va2 << endl;
	system("pause");
	return 0;
}</span>
6.13
<span style="font-size:18px;">前者是普通的傳值形參,
後一個是傳遞引用形參。</span>
6.14
<span style="font-size:18px;">swap 函數交換兩個值需要用到引用。
其實都可以用引用,如果這種情況不算的話。
void swap(int &a,int& b);
swap(2,3);//編譯失敗。
這個算不能用引用的例子嘛?
但是我稍微修改下形參就沒問題了。
void swap(const int& a,const int& b);
swap(2,3);
通過引用還是可以解決問題的。所以暫時沒理解題目的意思,除非你不想用引用。
</span>
6.15
<span style="font-size:18px;">避免數據拷貝工作,而且不需要修改s,故設置成常量引用,同時也爲了可以接受字符串字面值做參數。
因爲我們需要修復occurs,所以我不能是常量引用。
c可以爲引用,但是設置爲常量引用最好。
如果s是普通引用,那麼我們可以在函數體內對s進行修改。
如果occurs是常量引用,我們無法在函體內對它進行遞增。</span>
6.16
<span style="font-size:18px;">因爲採用了普通引用,所以是無法接受C字符串字面值作爲參數的。
改善:
bool is_empty(const string& s){
 return s.empty()
}</span>
6.17
<span style="font-size:18px;">#include <iostream>
#include <string>
using namespace std;
bool is_upper(const string& s){
	for (const auto x : s){
		if (isupper(x))
			return true;
		else
			continue;
	}
	return false;
}
void Upper(string &s){
	for (auto& x : s){
		x = toupper(x);
	}
}
int main(){
	string str = "I love C++ ";
	cout << "Call the test function " << endl;
	if (is_upper(str))
		cout << "Exist upper character " << endl;
	else
		cout << "Not exist upper character " << endl;
	cout << "Call the Upper function " << endl;
	Upper(str);
	cout << "Now the string is " << str << endl;
	system("pause");
	return 0;
}
形參類型肯定不同。
如果第二個函數是常量引用的話,就無法體內對字符串進行修改。
如果第一個函數不非常量引用話,那是無法接受字符串字面值作爲參數的。
或者試試這個函數:
string Upper(const string &s){
    auto temp= const_cast<string&>(s);
    for (auto& x : temp){
        x = toupper(x);
    }
    return temp;
}
</span>
6.18
<span style="font-size:18px;">1.bool Compare(martix&,martix&);
//功能肯定是比較兩個矩陣是否相等了。
2.vector<int>::iterator change_val(int, vector<int>::iterator);
//功能 應該是改變一個值。
</span>
6.19
<span style="font-size:18px;">a)不合法。參數過多。
b)合法。
c)合法。
d)合法。
</span>
6.20
<span style="font-size:18px;">無需修改參數並期望接受字面值參數。
可能無法接受字面值作爲形參。</span>
6.21
<span style="font-size:18px;">#include <iostream>
using namespace std;
int Bigger(const int& var, const int* ptr){
	return (var > *ptr) ? var : *ptr;
}
int main(){
	int va1 = 10, va2 = 12, *pva2 = &va2;
	cout << "The bigger is " << Bigger(va1, pva2);
	system("pause");
	return 0;
}
指針類型隨意,可以是普通的指針,也可以是指向常量的指針,甚至是常量指針。翻譯的質量。。 
</span>
6.22
<span style="font-size:18px;">#include <iostream>
using namespace std;
void Swap_pointer(int* (&ptr1), int* (&ptr2)){
	auto x = ptr1;
	ptr1 = ptr2;
	ptr2 = x;
}
int main(){
	int va1 = 10, va2 = 12, *pva1 =&va1, *pva2 = &va2;
	cout << "Before swaping ,two pointers's values are "
		<< pva1 << " " << pva2 << endl;
	cout << "Call the function " << endl;
	Swap_pointer(pva1, pva2);
	cout << "After swaping ,two pointers's values are "
		<< pva1 << " " << pva2 << endl;
	system("pause");
	return 0;
}
//這個地方我用的是指針的引用,那麼可以試試二級指針。
void Swap_pointer(int** ptr1, int** ptr2){
    //解引用二級指針得到的是什麼呢?
    auto x = *ptr1;
    *ptr1 = *ptr2;
    *ptr2 = x;
}
前面我已經提到過這個問題了,在函數體內對指針的直接修改是不反映到實參身上的,除非解引用。
題目要求我們交換指針的地址,如果只是在函數體內對指針自身進行修改,和傳值有何異同。 
</span>
6.23
<span style="font-size:18px;">#include <iostream>
using namespace std;
void Print(const int* p,int length){
	for (int i = 0; i != length; i++)
		cout << *p++ << " ";
}
void Print(const int* beg, const int* end){
	while (beg != end)
		cout << *beg++ << " ";
}
int main(){
	int i = 0, j[2] = { 0, 1 };
	Print(&i,1);
	cout << endl;
	Print(j, 2);
	Print(begin(j), end(j));
	system("pause");
	return 0;
}</span>
6.24
<span style="font-size:18px;">目的很簡單,遍歷一個10元素的數組。
但是因爲數組是被轉換爲指針了,編譯器不知道
你的數組真是容量,所以最好在設置一個參數
表示數組大小防止越界。
或者可以參考標準庫函數的做法。</span>
6.25


<span style="font-size:18px;">#include <iostream>
#include <string>
using namespace std;
int main(int argc,char** argv){
	argv[0] = "prog";
	argv[1] = " I";
	argv[2] = " love C++ ";
	argv[3] = 0;
	string str;
	cout << argc << endl;
	for (int i = 0; i !=3; i++)
		cout<< argv[i];
	cout << str << endl;
        system("pause");
        return 0;
 }</span>
6.26

參考25題。

6.27

<span style="font-size:18px;">#include <iostream>
#include <string>
#include <initializer_list>
using namespace std;
int Sum(initializer_list<int> iint){
	int sum = 0;
	for (auto x : iint)
		sum += x;
	return sum;
}
int main(){
	initializer_list<int> iint = { 1, 2, 3, 4, 5 };
	cout<<Sum(iint);
	cout << endl << Sum({ 2, 3, 45, 12 });
	system("pause");
	return 0;
}</span>
6.28
<span style="font-size:18px;">const string&</span>
6.29
<span style="font-size:18px;">視情況而定,若需要修改值可以聲明爲引用類型。否則則不必要。當然可以聲明爲常引用。</span>
6.30
<span style="font-size:18px;">error C2561: “str_subrange”: 函數必須返回值
參見“str_subrange”的聲明
</span>

6.31

<span style="font-size:18px;">實在沒理解題目的意思,返回的引用無效?
是說常量引用還是局部變量的引用?</span>
6.32
<span style="font-size:18px;"><span style="font-size:18px;">合法。把數組ia的元素設置爲0到9.</span></span>
6.33
<span style="font-size:18px;">#include <iostream>
#include <string>
#include <vector>
using namespace std;
void Print(vector<int> ivec,vector<int>::iterator beg,vector<int>::iterator end){
	if (beg == end)
		return;
	cout << *beg << " ";
	Print(ivec, beg + 1, end);

}
int main(){
	vector<int> ivec = { 1,2,3,4};
	Print(ivec, ivec.begin(), ivec.end());
	system("pause");
	return 0;
}
多多揣摩遞歸程序。
</span>

6.34

<span style="font-size:18px;">無影響。唯一的可能是輸入負值求階乘會出問題。</span>
6.35
<span style="font-size:18px;">val--的值是val的副本,值也是val,肯定不符合我們需求。</span>
6.36.37
<span style="font-size:18px;">auto func()->string(&)[10];
string  (&func1())[10];
typedef string(&Ref)[10];
using Ref = string(&)[10];
Ref func();
string str[10];
decltype(string)& func();
位置應是最好寫的。
</span>
6.38
<span style="font-size:18px;">auto arrptr(int i) ->int(&ref)[5];</span>
6.39
<span style="font-size:18px;">a)非法重載
b)非法重載
c)合法重載。
修改參數爲指向double類型的指針。</span>
6.40
<span style="font-size:18px;">b)默認實參後的所有的形參都要默認實參。</span>
6.41
<span style="font-size:18px;">a)不合法。
c)不符號初衷。因爲第二個實參用字符型進行初始化,但初衷是初始化第三個形參。</span>
6.42
<span style="font-size:18px;">#include <iostream>
#include <string>
#include <vector>
using namespace std;
string make_plural(size_t ctr, const string& word, const string& ending = "s"){
	return (ctr > 1) ? word + ending : word;
}
int main(){
	cout << make_plural(2, "success","es") << endl
		<< make_plural(1, "success") << endl
		<< make_plural(2, "failure") << endl
		<< make_plural(1, "failure") << endl;
	system("pause");
	return 0;
}</span>
6.43
<span style="font-size:18px;">a)內聯函數放在頭文件中。
b)普通函數放在源文件中。</span>
6.44
<span style="font-size:18px;">inline bool is_shorter(const string&s1, const string& s2){
	return s1.size() < s2.size();
}
</span>
6.45
<span style="font-size:18px;">遞歸的不能,篇幅太長的不建議,流程複雜的不建議,main函數不能。
其餘的隨意。</span>
6.46
<span style="font-size:18px;">不能。constexpr函數約定形參和和返回類型都是字面值類型。但是最後的返回語句肯定是不現實的。</span>
6.47
<span style="font-size:18px;">#include <iostream>
#include <string>
#include <vector>
//#include <cassert>
#define NDEBUG
using namespace std;
void Print(vector<int> ivec, vector<int>::iterator beg, vector<int>::iterator end){
	if(beg == end)
		return;
	cout << "vector's size is " << ivec.size() << " " << *beg << endl;
	Print(ivec, beg + 1, end);

}
int main(){
	vector<int> ivec = { 1, 2, 3, 4 };
	Print(ivec, ivec.begin(), ivec.end());
	system("pause");
	return 0;
}</span>
6.48
<span style="font-size:18px;">不合理。因爲退出循環的時候流的狀態肯定是無效的。</span>
6.49
<span style="font-size:18px;">類似於候選人,只要是重載的就可以參加候選。
可行,必須幹過小班長才勝任。。這個就叫可行。。。。。。</span>
6.50
<span style="font-size:18px;">a)二義性
b)c)d)最佳</span>

6.51
<span style="font-size:18px;">#include <iostream>
#include <string>
#include <vector>
#include <cassert>
using namespace std;
void Print(vector<int> ivec, vector<int>::iterator beg, vector<int>::iterator end){
	if(beg == end)
		return;
	cout << "vector's size is " << ivec.size() << " " << *beg << endl;
	Print(ivec, beg + 1, end);
}
void f(){
	cout << "無參數" << endl;
}
void f(int i){
	cout << "單參數" << i << endl;
}
void f(int va1, int va2){
	cout << "兩個int參數" << va1 << va2 << endl;
}
void f(double va1, double va2=3.14){
	cout << "兩個double參數" << va1 << va2 << endl;
}
int main(){
	//f(2.56,42);
	f(42);
	f(42, 0);
	f(2.45, 3.14);
	system("pause");
	return 0;
}</span>
6.52
<span style="font-size:18px;">a)等級 3
b)等級 4</span>

6.53

<span style="font-size:18px;">c)不合法。頂層const可以忽略。</span>
6.54

<span style="font-size:18px;">int func(int, int);
using pf = int(int,int);
vector<pf*> pvec;
試試decltype:
vector<decltype(func)*> pvec;
</span>
6.55

<span style="font-size:18px;">#include <iostream>
#include <string>
#include <vector>
using namespace std;
using pf = int(const int&, const int&);
int Add(const int &a, const int& b){
	int temp = a + b;
	return temp;
}
int (*pAdd) (const int&, const int&) = Add;
int Subtraction(const int& a, const int& b){
	int temp = a - b;
	return temp;
}
int(*pSub) (const int&, const int&) = Subtraction;
int Mult(const int& a, const int& b){
	int temp = a*b;
	return temp;
}
int(*pMult) (const int&, const int &)= Mult;
int Divide(const int& a, const int& b){
	int temp = a / b;
	return temp;
}
int(*pDivide)(const int&, const int&) = Divide;
int main(){
	vector<pf*> pvec = { pSub, pAdd ,pDivide,pMult};
	system("pause");
	return 0;
}</span>
6.56

<span style="font-size:18px;">#include <iostream>
#include <string>
#include <vector>
using namespace std;
using pf = int(const int&, const int&);
int Add(const int &a, const int& b){
	int temp = a + b;
	cout << "Call the Add function " << endl;
	return temp;
}
int (*pAdd) (const int&, const int&) = Add;
int Subtraction(const int& a, const int& b){
	int temp = a - b;
	cout << "Call the Sub function " << endl;
	return temp;
}
int(*pSub) (const int&, const int&) = Subtraction;
int Mult(const int& a, const int& b){
	int temp = a*b;
	cout << "Call the Mult function " << endl;
	return temp;
}
int(*pMult) (const int&, const int &)= Mult;
int Divide(const int& a, const int& b){
	int temp = a / b;
	cout << "Call the Divide function " << endl;
	return temp;
}
int(*pDivide)(const int&, const int&) = Divide;
int main(){
	vector<pf*> pvec = { pSub, pAdd, pDivide, pMult };
	auto iter = pvec.begin();
	auto eiter = pvec.end();
	cout << (*iter)(4, 2) //調用加法 
		<< (*(iter + 1))(4, 2)//調用減法
		<< (*(iter + 2))(4, 2)//調用乘法
		<< (*(iter + 3))(4, 2) << endl; //調用除法
	system("pause");
	return 0;
}</span>

後記:

56道題,好多。

End




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章