C++Primer 笔记(六)

第六章 函数

函数基础

  1. 实参与形参的区别 ,实参是调用函数时使用的参数,形参是函数定义时使用的参数,
  2. 形参列表可以为空,但不能省略
  3. 大多数类型都能用于函数的返回类型,void不返回任何值;且返回值不能是数组,但可以是指向数组的指针
  4. 局部对象分为自动对象和局部静态对象,自动对象只存在于块的执行过程中,局部静态对象第一次经过对象定义语句时初始化,到程序中止才销毁,不受块的约束。

传递参数

  1. 当形参是引用类型时,对应的实参被引用传递,当实参的值被拷贝给形参时,形参和实参时两个相互独立的对象,实参被值传递。
  2. 值传递时,函数对形参的操作不会影响实参,对指针参数也是同样的,不会改变实参指针存储的内容,形参和实参是两个不同的指针(但指向了同样的地址)。
  3. 引用传递时,对形参的操作实际上是对实参的操作,会改变主调函数的变量。

建议使用引用类型的形参替代指针,由于大型的容器在传值调用时需要拷贝,耗费的时间长,且有些容器根本就不支持拷贝的操作
如果函数无需改变引用形参的值,声明为常量引用 ,以防被修改掉值P189

bool isShroter(const string &a,const string &b)
{
	return a.size() < b.size()
}

  1. 使用引用形参返回额外信息,如果想要返回多个量,可以在调用时传入存储该量的变量的引用
  2. const形参和实参,用实参初始化形参时会忽略形参的顶层const,也就是向一个const形参可以传常量或者非常量对象;但不能用底层const实参来初始化非常量形参。
  3. 尽量使用常量引用,否则能传递给形参的实参类型会受到限制,如字符串字面量就无法传递给string &s类型的形参,并且调用该函数的其他函数在传递参数的时候也会有很大问题。
  4. 数组形参
    这三种都是传递指向数组首地址的指针
    形参格式
  • 管理数组的尺寸时,除了字符串里的’\0’标记,也可以显式传递大小,也可以将形参定义成数组的引用
void print(int (&arr)[10])
{
	for(auto elem:arr)
		cout<<arr<<endl;
}

但是局限在于,再传参数,只能传有10个整数的数组了…

  • 同样,不改变数组的内容的时候才设置成const形参
  1. 多维数组参数传递 传递的是指向数组首元素的指针,也就是一个指向数组的指针
void print(int (*maxsize)[10]){}
  1. 向main函数传递参数时,int main(int argc, char *argv[])第一个表示数组中字符串的数量,第二个参数的第一个元素指向程序的名字或者空字符串,之后的元素指向命令行中输入的参数,最后一个元素为0。
  2. 如果函数调用的是可变数量的参数,可以用initializer_list类型的形参: 如果输入的是可变数量的参数,可以用initializer_list类型的形参:
void error_msg(initializer_list<string> list1)
{
	for(auto beg=list1.begin();beg!=list1.end();++beg)
		cout<<*beg<<endl;
}

可以以error_msg({"func", expected, actual})调用,也可以error_msg({"func","Okay"})调用,

返回类型与return语句

  1. 无返回值函数如void其实是隐式地执行了返回的,并且可以在中途执行return来提前返回
  2. 有返回值的函数,C++只能保证返回的结果类型是正确的
  3. 不能返回局部变量或者是指向局部变量的指针,同样也不能返回局部临时量
  4. 引用返回 返回的是可改变值的左值
char &get_val(string &str, string::size_type ix)
{
	return str[ix];
}
string s="A+B";
getval(s,0)='a';
  1. 列表初始化返回值 ,返回的Vector对象根据大括号里面的列表进行初始化
vector<string> process()
{
	if(expected.empty())
		return {};
	else if(expected==actual)
		return {"funcX","okay"}; 
}
  1. 返回数组指针,因为数组不支持拷贝操作,所以返回一个数组的指针或者引用,有这么几种方法:
typedef int arrT[10];
arrT* func1(void;//声明一个返回数组指针的函数
auto func(void)->int(*)[10]//尾置返回类型
int a[10];
decltype(a)* func2(void);//用decltype关键字确定返回类型

函数重载

如果同一作用域几个函数名字相同但是形参列表不同,称之为重载函数编译器会根据传入的参数类型确定调用的是哪个函数。

  1. 需要注意的是,不允许两个函数除了返回类型之外其他的要素都相同
int lookup(const Account&);
bool lookup(const Account&);
  1. 形参用const修饰时,一个拥有顶层const的形参无法与没有顶层const的形参区别开来,不构成函数重载,但若形参是底层const,则可以将函数区别开来
  2. 调用重载的函数:编译器找一个与实参最佳匹配的函数,并生成调用该函数的代码;或者找不到匹配的函数,发出无匹配的错误;有多个函数可以匹配,发出二义性的错误;
  3. 不推荐在局部作用域内声明函数,因为局部变量覆盖之前的变量,编译器忽略了函数外部的同名实体,重载失效了。

特殊语言用途特性

  1. 默认实参 string screen(int len=5,int width=7)默认实参作为形参的初始值出现在形参列表中,需要注意的是一旦某个形参被赋予了默认值,它后面的所有新参都必须有默认值。考虑如下函数string screen(int len=7,int width)如果调用方式为screen(6),那6到底对应哪个形参?
  2. 局部变量不能对应默认实参,除此之外只要表达式的类型能转换为实参类型,该表达式就能作为实参。在给定作用域中形参只能被赋予一次默认实参,一般在函数声明是确定默认参数
  3. 内联函数和常量表达式函数:内联函数就是将函数在某个调用点展开,免去了保存寄存器,拷贝实参等等操作,消除了函数的运行开销,直接在函数名前加inline即可,但是inline函数只是向编译器发出的一个请求,编译器可以忽略。
  4. constexpr函数的返回类型和形参类型都得是字面值类型,有且只有一个return,编译器将这个函数的调用替换为其结果值。。constexpr函数的返回类型和形参类型都得是字面值类型,有且只有一个return,编译器将这个函数的调用替换为其结果值。
  5. 内联函数和constexpr函数可以多次定义,但是多个定义必须完全一致,因此常常将这两个函数定义在头文件中
constexpr size_t scale(size_t cnt)
{
	return new_sz()*cnt;
}

int arr[scale(2)]是合法的,因为scale(2)是常量表达式,如果传入的参数不为常量,则返回值也不会常量
6. assert预处理宏:assert(expr)expr求值,若表达式为假,则输出信息并中止程序,如果为真,assert什么也不做。另外,如果定义了NDEBUG宏,那么assert就什么也不做。定义NDEBUG可以避免很多调试的运行时开销

函数匹配

当重载函数的形参数量相等且可以通过其他类型转换而来时,有以下步骤来确定重载函数:

  1. 确定与被调用函数同名且调用点可见的候选函数
  2. 考察实参,确定能被这组实参调用的函数(包括满足类型转换规则的实参)
  3. 寻找最佳匹配函数,寻找类型最匹配的可行函数。该函数的每个实参的匹配都不劣于其他可行函数需要的匹配;至少有一个实参的匹配优于其他可行函数提供的匹配。
    具体的参数匹配有限集如下:
    1. 精确匹配
    2. 通过const转换实现的匹配
    3. 类型提升实现的匹配
    4. 算术类型转换实现的匹配

函数指针

  1. 函数指针指向的是函数而非对象,函数指针指向某种特定类型,函数类型由它的返回类型和形参类型共同决定,与函数名无关。
    声明int (*p)(int a, intb)
    指向函数名称p=lengthcompare
    调用bool b=p(x,y)
  2. 当我们把函数名作为一个值使用时,函数自动地转换为指针:pf=lengthcompare来将lengthcompare的地址赋给pf,更可以使用指针调用函数pf(int a, int b),无需解引用
  3. 重载函数的指针,具体指向的函数是通过指针类型决定的,从而在重载函数中选择。
  4. 可以将函数指针作为形参,函数作为实参来在函数的形参中调用另外一个函数,如
    Fcn(a, lengthcompare)
  5. 返回指向函数的指针,应当使用类型别名using PF = int(*)(int ,int);,这样就可以将PF作为函数的返回值,返回一个指向函数的指针了。
  6. 也可以将auto和decltype用于函数指针类型,返回的是一个函数类型,再加上*就可以声明函数返回一个函数指针了,如:
decltype(sumLength) *getFcn(const string&)

猜你喜欢

转载自blog.csdn.net/sinat_34753172/article/details/83653801