【C++】Essential C++ 第二章学习笔记

Essential c++ 第二章

这一章节的主要内容在于,教你学会如何正确的写一个函数。

1.如何处理用户的错误输入

由于用户输入可能会出现超出函数能够处理范围的情况,因此需要我们在写函数时候注意这一点

1.1结束函数运行

在cstdlib库中有一个exit函数可以使用,如果用户出现错误输入的时候可以直接结束函数,不过这种方法太过于激烈。

1.2 抛出异常

可以通过抛出异常的方式进行处理,这一部分会在第七章中进行介绍

1.3 返回值设置为bool类型

当用户输入错误的数据类型时,返回false,并且输出错误原因,以告诫用户。只有数据输入正确,才会返回true进行正确的运算。

2.如何查询每种数据类型的范围

在头文件#include中,有两个函数,用于分析一种数据类型的最大值和最小值

#include<limits>
int max_int = numeric_limits<int>::max();
int min_double = numeric_limits<double>::min();

3.参数列表应该传入数值还是传入地址

3.1 传址和传值的规则

地址传递主要有两种应用情况,一种是需要在函数内部修改这个传递的变量;另外一种是这是一种复杂的数据类型,进行数值重新复制会消耗很多时间,传址更有效率。如果不是以上这两种情况,不建议使用传递地址的方式传入变量,比较不安全

3.2引用和指针

指针能够直接的指向变量的地址,指针可以赋予空值,因此在使用前应该检查当前指针是否为空指针。

引用也能够指向变量的地址,本质是一种常量指针,指向一个变量的地址之后,就不允许改变了,也称为与这个变量绑定了。因为引用是一种绑定的机制,因此引用不会出现空值的情况,使用前不需要检测是否为空

3.3引用使用技巧

引用主要有四种使用技巧

(1)传递希望在函数中能够修改的变量,如交换函数

swap(int &a,int &b)
{
    int c;
    c = a;
    a = b;
    b = c;
}

(2)传递复杂的类,希望减少复制的时间

(3)使用const 的引用作为参数进行传递,不希望这个复制的变量在函数内被人修改,如:

void (const myClass& test)
{
    
}

(4) 实现多个参数作为返回值。在函数参数列表中加入一个引用类型的变量,把数据存入这个引用类型变量中,就能够实现多个参数作为返回值。

4.内存管理

在c++中,变量主要有三种类型的储存位置

4.1栈区

栈区存储变量由编译器自动生成,是存放临时变量的地方,这些变量位于local scope

4.2堆区

堆区的变量是由程序员定义生成,通过new关键字进行定义,必须使用delete关键词才能从堆区删除,否则变量一直无法被释放,形成内存泄露的问题

4.3全局变量区

全局变量去主要用于存放位于file scope的变量,还有static的变量,因为这些变量的生存域是整个文件,因此这些变量在main开始运行之前就被定义好了。

这里顺便提及一下,static具有两个含义,一个是静态变量作用区域为整个文件,另外一个含义是,静态变量具有隐藏属性,不能被其他文件所发现,不能使用extern关键字进行扩展。

5.默认值

有的时候,有些参数是可有可无的,就可以把这些参数做成默认值,只有需要用户指定的时候,才进行输入。

值得注意的是,默认值在声明和定义中只能出现一次,因为头文件透明度比较高,因此我们一般将默认值放在声明里面,也就是头文件里面。

6.提高函数运行效率

6.1在函数中使用局部静态变量

有时候我们在函数中会出现某个变量需要重复计算的情况,比如返回斐波那契数列,每次调用函数都需要用循环从头生成一遍,使用局部静态变量之后,可以节省这个重新生成的时间。

将斐波那契数列存放在static的vector中,只有输入的index大于数列长度的时候,才重新生成没有的那一段,非常节约计算时间。

6.2使用inline函数

inline函数运行机制是直接把函数复制到引用位置,而不是在内存中生成代码块反复调用,反复调用会比较慢,这是一种空间换时间的做法。

6.2.1 inline函数使用规则
  • inline写在函数最前面
  • inline函数必须写在头文件中,否则main函数找不到他的定义
6.2.2 inline使用的场景
  • 函数体非常短
  • 这个函数被反复调用
  • 这个函数中没有出现递归的算法

7. 减少函数名称,使函数数量更少的方法

7.1 重载

重载函数是指变量名一样,参数列表不同的函数。这种函数主要用于处理思路一样,但是具有不一样的处理对象的函数的合并。

比如比较两个int的最大值、比较两个double的最大值,就可以使用重载的方法,两个函数定义为同名函数。


int max(int a,int b);
double max(double a,double b)

注意重载必须是参数列表不同才行,返回值不同无法构成重载。

7.2 模板

模板适用于函数处理思路一样,处理流程一样,但是处理变量类型不同的函数的合并。比如7.1的例子,除了使用重载以外,还可以使用模板的方法。

template <typename T>
T max(T a,T b);

7.3函数指针

函数指针适用于,函数内部需要调用外部函数,但是这些外部函数比较相似,如果要为每个调用的外部函数专门另写一个函数,会需要创建很多的函数。如果使用函数指针的方法,就可以使用一个函数,其他外部引用函数采取指针的策略,减少了函数个数

7.3.1 函数指针的写法

//原来的函数
int max(int a,int b)
{
    return (a>b?a:b);
}

//定义函数指针
int (*f)(int a,int b);

//函数指针指向新的地址
f = max;
//以函数指针的方式调用
f(1,2);

7.3.2 使用索引管理函数指针
//01 假设有这么几个函数要被其他函数调用,返回长度为index的数列
const vector<int>* Fibno(int index);
const vector<int>*Pell(int index);
const vector<int>*Pentagonal(int index);

//02 定义函数指针
const vector<int>* (*ptr)(int );
//注意ptr和*一定要使用括号,这是程序优先级的问题

//03 建立数组进行管理
const vector<int>**ptr_arraty[])(int)=
{
    Fibno,Pell,Pentagonal
}
//04 为了区分每个索引是什么,要给索引赋予意义
enum ns_ptr
{
    ns_Fibno,
    ns_Pell,
    ns_Pentagonal,
}

//05 使用
const vector<int> (*f)(int)=ptr_array[ns_Pell];
const vector<int> * test
=f(10);

7.3.3 一个比较不恰当的例子
#include<iostream>
using namespace std;

//01 函数指针怎么用
//01-1 假设我有一些专门打印序列名字的函数
void fibonPrint()
{
	cout << "this is fibon" << endl;
}

void lucusPrint()
{
	cout << "this is lucus" << endl;
}
void pellPrint()
{
	cout << "this is pell" << endl;
}
void trianglePrint()
{
	cout << "this is triangle" << endl;
}
void squarePrint()
{
	cout << "this is square" << endl;
}
void pentPrint()
{
	cout << "this is pent" << endl;
}


//01-2 假如我有一个专门处理数列的函数(假设返回数列第几个数字),处理完了以后打印数列名字

//如果直接写,要写多个函数,处理fibno函数的打印fibno名字,就要写多个函数
void func_fibno()
{
	//随便什么处理

	fibonPrint();
}
void func_pell()
{
	pellPrint();
}

//使用函数指针,可以用一个函数代表全部的函数
void func2(void (*ptr)(void))
{
	ptr();
}

//02 函数指针使用的进一步优化

//用数组来储存函数指针,这样就可以用序列来访问了
void (*ptr_array[])(void) =
{
	fibonPrint,lucusPrint,pellPrint,trianglePrint,squarePrint,pentPrint
};

//用枚举给每个序列赋予意义

enum ns_type
{
	ns_bibno,
	ns_lucus,
	ns_pell,
	ns_triangle,
	ns_square,
	ns_pent
};


int main()
{
	//解决问题:有同样参数列表、同样返回值的函数,在其他函数中需要调用这些函数,就可用函数指针的方法来
	//减少函数的定义数量。因为相当于你要为每个调用的函数写一个专门的函数。

	//01 函数指针的使用
	//直接写各个函数的处理+打印函数
	func_fibno();
	func_pell();

	//用函数指针就可以一个函数代表了,与上面效果一样,但是更简单
	func2(fibonPrint);
	func2(pellPrint);
	
	//02 用序列的方法优化函数指针,不然要记住很多函数的名字

	//用序列对函数指针进行管理,更加的方便
	func2(ptr_array[0]);
	func2(ptr_array[ns_pell]);
	return 0;
}



8.头文件

8.1关于函数

函数声明放置在头文件中,定义放置在程序中。但是inline函数特殊,要放在头文件中

8.2关于全局变量

全局变量要想被其他文件调用,应该在头文件中加入extern,当程序中出现这句话的时候,他的意思是,在程序中有存在这样一个全局变量,但是我不知道他在哪,你去其他文件中找找看。其他文件中存放的就是这个全局变量的定义。全局变量的定义不能放在头文件中,头文件只能放置声明,所以要加extern。但是const变量特殊,要把定义放在头文件中,因为const变量出了文件之后就失效了,所以不能放在其他源文件中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值