C++ Primer 笔记+习题解答(六)

今天是第六篇笔记,主要内容是函数。现在的节奏基本就是一天读书一天笔记总结。

若有错误 请指正 谢谢

0.引言:

 1.函数:

是一个命名的代码快,通过调用函数可以执行相应的代码。函数通常会返回一个结果。

 2.构成:

返回类型 ,函数名 ,形参列表 ,函数体。

 3.调用:

通过调用运算符(())也就是一对圆括号。调用运算符作用于一个表达式,表达式通常是函数名或者指向函数的指针。圆括号内放实参表,用于初始化对应的形参。看清楚,是初始化,就是我们前面所说的变量初始化,所以理解这个后面就很省力了。

 4.流程:

1.用实参初始化形参列表。2.将控制权从主调函数移交给被调函数,也就是说主调函数的执行被中断,被掉函数开始执行,当被调函数执行完毕,控制权移交回主调函数。

 5.return 语句:

若有值的话,返回值并且结束被调用函数的执行。没有值直接结束函数的执行。

 6.形参实参:

存在一一对应的关系。形参一定要被初始化,但是不规定初始化的顺序,也就是前面所说的求值顺序。

 7.形参列表:

可以为空,但是不可以省略。空形参表有两种形式:

void fun();
void fun(void);

 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.传值:

司空见惯的方式,不赘述。这个地方我们把传地址的方式也归结到穿值的方式。其实这样的做法才是透彻理解的行为。我一开始是区分的,后来有一天我遇到了二叉树的创建问题,结合今天的理解,才算是明悟。

指针形参:在函数体内对指针自身进行修改是不影响源指针的,这个地方虽然是指针,当时当你对指针进行修改的时候,就是传值传递,传的是一份拷贝。只有当你在对指针进行解引用操作,修改才会涉及变量自身。

示例:

void func(int *p){
  p=nullptr;
  *p=0;
}
int i=10,*ptr=&i;
func(ptr)
这个地方我就是想强调对ptr的操作就是传值的方式,也就是说你在体内无论如何修改p,都不会影响ptr,因为这就是指针间的传值拷贝。只有指针涉及到解引用的操作的时候才会影响i.如果你还没看懂我的意思,那么你可以看一下为什么二叉树创建会用二级指针或者是指针的引用。因为在二叉树的创建里涉及到指针的自身操作,如果是普通的传值,在里面的操作是不会影响实际参数的。所以你的创建肯定是失败的。这就是为什么把这个操作归结到传值的原因。

  2.传引用:

也是司空见惯的方式。刚刚上面我提到指针的引用,应该是归结到引用这里的。当引用是形参的时候,这个时候和形参进行交互实际上就是别名的操作了,对别名的操作统统会在实参上得到反馈。而最大的好处就是提高效率,因为避免了拷贝的行为。

 3.返回多个值:

不要被这句话误导了,函数还是只有一个返回值。这个地方的返回多个值的意思是把函数体内的信息带出来,因为函数体内的东西是有声明周期和作用域的。

如何把函数内的信息带出来呢?

两个方式:1.自己定义一个数据类型,比如类,你可以返回一个类。2.用引用传递一个额外参数,通过这个行为,我们都可以让void函数返回一个值,只是通过的渠道不通而已。

 4.const 形参和实参:

这个地方有必要理下顶层const和底层const。

当用实参初始化const形参的时候,其中的顶层const是可以被忽略的。也就是说我们可以用常量或者非常量初始形参。

示例:

int var=10;
const int cvar=var;//顶层const。
const int ccvar=10;
这个地方只是举了初始的例子,函数内是一样的。我们可以看到,虽然类型不同,但是初始化完全没我问题。

甚至忽略顶层const还会导致一个重载函数的错误。

示例:

void fun(int i);
void fun(const int i);
//这是两个函数嘛 ?
实际上编译器会认为这是一个函数,因为他们两的形参是可以互通的,而重载的区分是通过形参的数量和类型区分的。

 5.指针或引用形参与const:

我们可以使用非常量初始一个底层const对象,但是反过来是不可以接受的。同时普通引用必须用同类型进行初始化。

示例:

int i=10;
const int &ref=i;
const int *p=&i;
int *ptr=p;//错。
int &re=10;//错误。
int &r=ref;//错误。 
同样的,在函数里初始化是同样的要求。

 6.数组形参:

数组的两个特殊性质:1.不能拷贝。2.使用其时会转换为指针。

不能拷贝要求我们不能进行传值调用,转换告诉我们相当于传递指针。

考虑下面三个声明:

void print(const int*);
void print(const int[]);
void print(const int[10]);
算是重载嘛?不算。因为他们都是一个类型:const int*.

因为数组以指针传递的方式传递给函数,但是函数是当成指针理解,并不知道数组的具体大小,为了防止越界行为发生,我们提供了三种改进措施。

  1.显式的提供一个委外参数表示数组大小。

示例:

int arr[10]={0};
void print(const int[],int size);
最简单的方式,我们直接提供数组大小的信息就可以了。

  2.使用标记指定的数组长度,要求数组本身拥有结束字符。最常见的就是c-style 字符串了。自动空字符'\o'。

示例:

char cstr[]="C++";
void print(const char* p){
  while(*p)
    cout<<*p++<<" ";
}
#include 
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;
}

  3.借用标准库函数:

采用begin 和 end作为形参:

void print(const int *beg,const int *end);
int arr[10]={0};
调用:
print(begin(arr),end(arr));
看过前面章节的人肯定知道这两个函数的神奇。

 7.数组形参和const:

提到const,肯定是只读相关的。当我们不需要对数组中的元素进行写值的时候,定义成执行常量的指针最好。

 8.数组引用形参:

看个例子就明白了.

示例:

void print(int (&ref)[10]);
请务必在引用符号前写上括号。我想不用区分数组的引用和引用的数组,后者根本是一个子虚乌有的东西。

 9.传递多维数组:

前面也提过,比存在多维数组这个东西,其实就是数组的嵌套。在一维数组中,我们传递进去的是数组名,也可以理解成指向首元素的指针。当传递多维数组的时候,我们可以类比下。

多维数组中的每个元素自身都是一个数组,如果你要传递一个东西指向数组的第一个元素。数组第一个元素还是数组,所以我们传递一个指向数组的指针就可以了。

void print(int (*p)[10]);
void print(int [][10]);//简单粗暴的写法了。 
说是说传递二维数组,传递进去的是指向数组的指针。

 10.含有可变形参的函数:

为了提供了不同数量的形参,C++11提供了两种方法:

1.如果未知数量的实参类型相同,可以使用一个标准库类型当作形参。

2.若实参类型不同,那么可以需要编写可变参数模版,暂时不表。

C++还提供了一种特殊的形参类型,省略符形参。我们可以用它传递可以变数量的实参,但是它一般用于C函数的接口函数。

initializer_list形参:这个一个标准库类型,具体使用方法可以参考向量。肯定要头文件,附加类型信息等。此标准库类型是用于表示某种特殊类型的数组。函数此形参当然也可以同时含有其他形参。

省略符形参:使用了名为varargs的c标准库功能。一般仅仅用于c车c++ 共有的类型,当使用类类型的时候可能会产生错误。

省略符形参只能出现在形参列表的最后一个位置,所以语法格式越就两种了.

示例:

1.void func(parameter-lisnt,...);
2.void func(...);
1中的,是可选的。
省略符部分是不进行类型检查的。

2.返回类型和return 语句:

0.格式:

1.return ;
2.return expression;

1.无返回值

void不要求非要写上return语句,因为编译器为隐式的执行。但是一条是肯定的,此种return语句只能用于void函数中。

当然void也可以用第二种格式,只是比较少见而已,而且用发有一些要求。要求就是后面的表达式必须是返回void函数。

2.有返回值:

提供函数的返回结果,只要写了就必须加上返回值。其中返回和返回类型必须是相关类型。

3.值的返回:

0.引子:

还是类似于变量初始化,返回的值用于初始化调用点处的一个临时量,该临时量就是函数调用的结果。

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

小菜:返回类类型和调用运算符。意思就是使用函数的返回结果再次调用函数。一般要求函数的返回类型是引用或者相关指针类型。

1.引用返回左值:

调用返回引用的函数就可以得到左值,左值就可以往里面写东西的嘛。

示范:

int& func(const int &i){
  return i;
}
int k=func(3);
func(3)=10;//这个就是返回左值的例子。

2.返回列表初始值:

一看就知道和C++11相关。C++11规定,函数可以返回花括号包围的初始值列表。若是内置类型,列表中最多有一个值。但是自定义类型,那就看具体定义。比如vector,可变长的。

3.main函数的返回值:

允许main函数无返回值,因为隐式的会执行。一般来说返回0表示执行成功,非0值的具体含义由具体机器决定。

我们可以使用头文件cstdlid中定义的两个预处理变量。

示例:

return EXIT_FAILURE;
return EXIT_SUCCESS;
//主要包含头文件。

4.递归插曲:

1.自己调用自己。2.main函数不能递归。3.递归要有个出口,不然会演变为递归循环。

5.返回数组的指针:

我们是知道的,数组是不能拷贝的,所以无法返回值。但是可以返回数组的指针或者引用。为了简化书写,我们通常会使用别名进行简化

示例:

typedef int arr[10];
using arr=int[10];
//两种方法都可以起别名。
arr* func(int i);
不简化的书写格式:
type (*func(int i)) [10];
int (*func(int i))[10];
比较麻烦的写法,不好记忆。

我们可以使用位置返回类型进行优化。顾名思义,尾巴放上返回类型,着实新奇。

auto func(int i) ->int (*)[10];
原来写类型的地方换成了auto,在后面组合箭头运算符写上了真正的类型。C++11 规定,任何函数都可以使用尾置返回类型,但是一般的也没必要使用,直接一步到位就可以了,复杂的可以写成这个样子。

同样的可以用一下decltype 进行优化。

int arr[10]={0};
decltype(arr)* func(int i);
这个地方就强调一点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语句并且必须有。

因为在执行初始化的时候,编译器把函数调用转换为值替换,所以是隐式内联。

constexpr int func(int i){
   return 42*i
}
func(2)可以用作常量。
func(a)不可以用作常量。这个地方就可以清楚什么叫字面值类型了吧。
constexpr函数不一定返回常量类型。

内联函数和constexpr函数可以多次定义,考虑到特殊性,一般把他们定义在头文件中,注意是定义,不是声明。

4.调试帮助:

1.assert(expr) 对expr进行求值,若为0,则assert执行并终止程序,否则不执行。

2.asset定义在头文件中cassert中。不可以定义同名对象或函数等其他实体。

3.NDEBUG:assert 的行为取决于NDEBUG的状态,定义了它,那么assert 不执行,否则assert执行运行时检测。

默认是不定义NDEBUG。

4.我了解的不多,用到的时候慢慢学习吧。

6.函数匹配:

候选函数→可行函数→最佳匹配。

7.函数指针:

1.函数指针:

是指向函数而非对象。函数的类似由返回类型和形参列表构成,与函数名无关。

我们不可以返回函数类型,但是可以返回执行函数的指针。

void print(int i);
void (*pf)(int i);
pf=print;
pf=&print;
调用时都可以接受。
调用时无需解引用。指向不同类型的函数指针不存在转换规则。

2.重载和函数指针:

void print(int i);
void print(double i);
void (*pf)(int i)=print;//天经地义
void (*ppf)(int *)=print;//不匹配。
函数指针形参:函数自己不可以当做形参,但是函数的指针就可以。用在形参时候可以下简化书写。常见的就是别名等了。

当然C++11还有别的工具。用decltype推断出来的类型需要自己加上*号。

返回指向函数的指针:可以用位置类型或者别名优化下格式书写。

tips:decltype用于函数指针类型时,它返回的是函数类型而非指针类型记得自己显式加上*号。

8.习题解答:

6.1

区别:实参是用来初始化形参的。
形参是调用函数时我们创建的。
实参是我们调用函数时传递进去初始化对应形参。
6.2
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;
}
6.3
#include 
#include
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;
}
6.4
#include 
#include
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 <
6.5
#include 
#include
using namespace std;
/*int abs_func(int var){
	return abs(var);
}*/
template
T abs_func1(T var){
	if (var < 0)
		return var *= -1;
	else
		return var;
}
int main(){
	long long sum = 0;
	int val = 0;
	cout<(-3);
	cout << abs_func1(-10.2);
	system("pause");
	return 0;
}
6.6
局部变量包含后两者。
形参:在定义函数时定义的变量。
局部静态变量:在函数体内部创建的静态变量,在函数体可见,但是生命周期延迟到程序结束。
int func(int var){  //var是形参、
    static int i=var;//i是静态局部变量、
    ++   i;
    return i
} 
6.7
#include 
#include
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;
}
6.8
#ifndef CHAPTER_H
#define CHAPTER_H
#include 
int fact(int val);
#endif
6.9
//chapter.h
#ifndef CHAPTER_H
#define CHAPTER_H
#include 
int fact(int val);
#endif
//fact.cpp
#include "chapter.h"
int fact(int val){
    return val+1;
}
//main.cpp
#include "chapter.h"
#include
using namespace std;
int main(){    
    for (int i = 0; i != 10; i++){
        cout << fact(i) << " ";
    }
    system("pause");
    return 0;
}
6.10
#include
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;
}

6.11

#include 
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;
}
6.12
#include 
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 = "<
6.13
前者是普通的传值形参,
后一个是传递引用形参。
6.14
swap 函数交换两个值需要用到引用。
其实都可以用引用,如果这种情况不算的话。
void swap(int &a,int& b);
swap(2,3);//编译失败。
这个算不能用引用的例子嘛?
但是我稍微修改下形参就没问题了。
void swap(const int& a,const int& b);
swap(2,3);
通过引用还是可以解决问题的。所以暂时没理解题目的意思,除非你不想用引用。
6.15
避免数据拷贝工作,而且不需要修改s,故设置成常量引用,同时也为了可以接受字符串字面值做参数。
因为我们需要修复occurs,所以我不能是常量引用。
c可以为引用,但是设置为常量引用最好。
如果s是普通引用,那么我们可以在函数体内对s进行修改。
如果occurs是常量引用,我们无法在函体内对它进行递增。
6.16
因为采用了普通引用,所以是无法接受C字符串字面值作为参数的。
改善:
bool is_empty(const string& s){
 return s.empty()
}
6.17
#include 
#include 
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(s);
    for (auto& x : temp){
        x = toupper(x);
    }
    return temp;
}
6.18
1.bool Compare(martix&,martix&);
//功能肯定是比较两个矩阵是否相等了。
2.vector::iterator change_val(int, vector::iterator);
//功能 应该是改变一个值。
6.19
a)不合法。参数过多。
b)合法。
c)合法。
d)合法。
6.20
无需修改参数并期望接受字面值参数。
可能无法接受字面值作为形参。
6.21
#include 
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;
}
指针类型随意,可以是普通的指针,也可以是指向常量的指针,甚至是常量指针。翻译的质量。。 
6.22
#include 
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;
}
前面我已经提到过这个问题了,在函数体内对指针的直接修改是不反映到实参身上的,除非解引用。
题目要求我们交换指针的地址,如果只是在函数体内对指针自身进行修改,和传值有何异同。 
6.23
#include 
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;
}
6.24
目的很简单,遍历一个10元素的数组。
但是因为数组是被转换为指针了,编译器不知道
你的数组真是容量,所以最好在设置一个参数
表示数组大小防止越界。
或者可以参考标准库函数的做法。
6.25


#include 
#include 
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;
 }
6.26

参考25题。

6.27

#include 
#include 
#include 
using namespace std;
int Sum(initializer_list iint){
	int sum = 0;
	for (auto x : iint)
		sum += x;
	return sum;
}
int main(){
	initializer_list iint = { 1, 2, 3, 4, 5 };
	cout<
6.28
const string&
6.29
视情况而定,若需要修改值可以声明为引用类型。否则则不必要。当然可以声明为常引用。
6.30
error C2561: “str_subrange”: 函数必须返回值
参见“str_subrange”的声明

6.31

实在没理解题目的意思,返回的引用无效?
是说常量引用还是局部变量的引用?
6.32
合法。把数组ia的元素设置为0到9.
6.33
#include 
#include 
#include 
using namespace std;
void Print(vector ivec,vector::iterator beg,vector::iterator end){
	if (beg == end)
		return;
	cout << *beg << " ";
	Print(ivec, beg + 1, end);

}
int main(){
	vector ivec = { 1,2,3,4};
	Print(ivec, ivec.begin(), ivec.end());
	system("pause");
	return 0;
}
多多揣摩递归程序。

6.34

无影响。唯一的可能是输入负值求阶乘会出问题。
6.35
val--的值是val的副本,值也是val,肯定不符合我们需求。
6.36.37
auto func()->string(&)[10];
string  (&func1())[10];
typedef string(&Ref)[10];
using Ref = string(&)[10];
Ref func();
string str[10];
decltype(string)& func();
位置应是最好写的。
6.38
auto arrptr(int i) ->int(&ref)[5];
6.39
a)非法重载
b)非法重载
c)合法重载。
修改参数为指向double类型的指针。
6.40
b)默认实参后的所有的形参都要默认实参。
6.41
a)不合法。
c)不符号初衷。因为第二个实参用字符型进行初始化,但初衷是初始化第三个形参。
6.42
#include 
#include 
#include 
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;
}
6.43
a)内联函数放在头文件中。
b)普通函数放在源文件中。
6.44
inline bool is_shorter(const string&s1, const string& s2){
	return s1.size() < s2.size();
}
6.45
递归的不能,篇幅太长的不建议,流程复杂的不建议,main函数不能。
其余的随意。
6.46
不能。constexpr函数约定形参和和返回类型都是字面值类型。但是最后的返回语句肯定是不现实的。
6.47
#include 
#include 
#include 
//#include 
#define NDEBUG
using namespace std;
void Print(vector ivec, vector::iterator beg, vector::iterator end){
	if(beg == end)
		return;
	cout << "vector's size is " << ivec.size() << " " << *beg << endl;
	Print(ivec, beg + 1, end);

}
int main(){
	vector ivec = { 1, 2, 3, 4 };
	Print(ivec, ivec.begin(), ivec.end());
	system("pause");
	return 0;
}
6.48
不合理。因为退出循环的时候流的状态肯定是无效的。
6.49
类似于候选人,只要是重载的就可以参加候选。
可行,必须干过小班长才胜任。。这个就叫可行。。。。。。
6.50
a)二义性
b)c)d)最佳

6.51
#include 
#include 
#include 
#include 
using namespace std;
void Print(vector ivec, vector::iterator beg, vector::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;
}
6.52
a)等级 3
b)等级 4

6.53

c)不合法。顶层const可以忽略。
6.54

int func(int, int);
using pf = int(int,int);
vector pvec;
试试decltype:
vector pvec;
6.55

#include 
#include 
#include 
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 pvec = { pSub, pAdd ,pDivide,pMult};
	system("pause");
	return 0;
}
6.56

#include 
#include 
#include 
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 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;
}

后记:

56道题,好多。

End




你可能感兴趣的:(读书笔记,C++,Primer,读书笔记)