第六章重点知识点总结-----函数

一、函数基础

1、

int fun(int v1,v2);       //错误
int fun(int v1,int v2);  //正确

2、大多数的类型都可以用作函数的返回类型,但是数组类型或者是函数类型不可以,但是可以用指向数组或函数的指针

3、只存在于执行期间的对象是自动对象,普通的局部变量也是自动对象

4、分离式编译允许我们把程序分隔到几个文件中去,每个文件单独编译

二、参数传递

1、传值参数

  (1)、当初始化一个非引用类型的变量时。初始值被拷贝给变量。对变量的改动不会影响初始值

int add(int n)
{
	n = 4;
	return n;
}
int main()
{
	int val = 5;
	printf("%d\n",add(val));  //4
	printf("%d\n",val);         //5
	system("pause");
	return 0;
}

  (2)、指针作为形参,函数接受一个指针,通过指针可以改变其所指对象的值,但是作为实参的指针本身并未被改变

  (3)、c++建议使用引用类型的形参代替指针

2、传引用参数 

#include <iostream>
using namespace std;
void ad(int &a)
{
	a = 32;
}
int main()
{
	int val = 5;
	ad(val);
	cout << val << endl;     //32

	system("pause");
	return 0;
}

   (1)、使用引用传参的好处:避免拷贝;如果有的时候传参的对象占的内存比较大,使用拷贝的传参效率会比较低

   (2)、使用引用可以返回额外信息

int val = 36;
int ad(int &a,int &val)
{
	a = 32;
	val = a + val;
	return a;
}
int main()
{
	int b = 5;
	ad(b,val);
	cout << val << endl;   //68

	system("pause");
	return 0;
}

   (3)、如果函数无需改变引用形参的值,最好将其声明成常量引用

3、const形参和实参

  (1)、当用实参初始化形参时会忽略掉顶层的const

  • void fcn(const int i);
  • void fcn(int i);    //错误

  (2)、指针或引用形参与const举例

  • int i=42;
  • const int *cp=&i;   //正确
  • const int &r=i;      //正确
  • const int &r2=42;  //正确
  • int *p=cp;        //错误
  • int &r3=r;        //错误
  • int &r4=42;     //错误

  (3)、我们不能把const对象、字面值或者需要类型转换的对象传递给普通的引用形参

  • string::size_type find_char(string &s,char c,string::size_type &occurs)
  • find_char("hello world",'o',ctr);     //错误

4、数组形参

  (1)、数组传参实际上是传递的指向数组首元素的指针

  (2)、数组是以指针的形式传递给函数的,所以一开始函数并不知道数组的确切尺寸,管理指针形参有三种常用的技术

  • 使用标记指定数组的长度---数组的最后一位是空字符
  • 使用标准库规范------begin、end
  • 显示传递一个数组大小

  (3)、 当将多维数组传递给函数时,真正传递的是指向数组首元素的指针

5、可变参数

  • 实参类型不同时,采用可变参数模版
  • 实参类型相同时,传递initializer_list标准库类型(用于表示某种特定类型的值的数组)
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int sum(initializer_list<int> li);
int main(int argc, char *argv){
	initializer_list<int> la{ 2, 3, 5, 6, 7 };
	int sum0 = sum(la);
	cout << "la列表中元素之和为:" << sum0 << endl;
	system("pause");
	return 0;
}
int sum(initializer_list<int> li){
	int sum = 0;
	for (auto beg = li.begin(); beg != li.end(); ++beg)
		sum += *beg;
	return sum;
}

三、返回类型和return 

   (1)、不要返回局部对象的引用或指针

   (2)、引用返回左值

char &get_val(string &s1, string::size_type ix)
{
	return s1[ix];
}

int main()
{
	string s("hello");
	get_val(s, 0) = 'H';
	cout << s << endl;  //Hello

	system("pause");
	return 0;
}

   (3)、列表初始化返回值

vector <string> prosee()
{
	...
		...
	return{ "hello1","okey" };

}

   (4)、返回数组指针:因为数组不可拷贝,所以函数也不能返回数组,只能返回数组的指针或者引用

typedef int arr[10];
arr * func(int i);  //返回一个指向含有10个整数的数组的指针

int(*func(int i)) [10]; //返回数组的函数的写法

使用decltype(需要知道函数返回的指针指向哪个数组)
decltype(指向的数组) *arrT(parameter_list); 

四、函数重载

 (1)、顶层const不影响传入函数的对象(拥有顶层const的形参无法与没有顶层的形参区分);

  • void fcn(const int i);
  • void fcn(int i);    //错误

 (2)、 const_cast的作用:把一个常量变成非常量

函数形参是常量,但是实参不是常量,并且想返回得到一个非常量的普通引用可以使用const_cast强制转换得到

 (3)、函数声明最好不要置于局部作用域内,因为在内层作用域内声明名字,将会隐藏外层作用域中声明的同名实体,且在不同的作用域中无法重载函数。名字查找发生在类型检查之前。

五、函数指针

(1)、 函数指针指向函数而不是对象,函数指针指向某种特定类型,函数的类型由返回类型和形参类型共同决定,与函数名无关。

bool lengthCompare(const string &, const string &);
bool (*pf)(const string &, const string &); //pf指向函数的指针,pf两端的括号必不可少。
bool *pf(const string &, const string &); //声明一个函数pf,返回bool*
pf = lengthCompare; //pf指向名为lengthCompare的函数
pf = &lengthCompare; //等价语句,&可选

(2)、编译器通过指针类型决定选用哪个重载函数,指针类型必须与重载函数中的某一个精确匹配

(3)、虽然不能定义函数类型的形参,但形参可以是指向函数的指针,或形参的函数类型直接转换为指针可以直接将函数作为实参使用,它会自动转换为指针

void useBigger(const string &s1,const string &s2, bool pf(const string &,const string &));
//等价声明
void useBigger(const string &s1,const string &s2, bool (*pf)(const string &,const string &));
//自动将函数转换成指向该函数的指针
useBigger(s1,s2, lengthCompare);

(4)、使用typedefdecltype可以简化函数指针;decltype返回函数类型,,不会将函数类型自动转换成指针类型

//Func 和Func2为函数类型
typedef bool Func(const string &, const string &);
typedef decltype(lengthCompare) Func2;
//Funcp和Funcp2 指向函数的指针
typedef bool *Funcp(const string &, const string &);
typedef decltype(lengthCompare) *Funcp2;

(5)、编译器不会自动将函数返回类型当成对应的指针类型处理必须将返回类型写成指针形式使用类型别名,声明一个返回函数指针的函数

六、注意点

(1)、一旦某个实参被赋予了默认值,它后面的所有形参都必须有默认值

(2)、函数调用时实参按照位置解析,默认实参负责填补函数调用缺少的尾部实参;

(3)、将规模较小的操作定义为函数,阅读理解较为容易、确保行为统一、修改函数更为方便、可重复利用,但是调用函数比表达式求值要慢,因此提出内联函数
内联函数:在函数的返回类型前加关键字inline即可,函数指定为内联函数,即将它在每个调用点内联的展开。
内联机制主要用于优化规模较小,流程直接,频繁调用的函数。

(4)、constexpr函数是指能用于常量表达式的函数;

  • 函数的返回类型和形参类型都必须为字面值常量;
  • 函数体中有且只有一条return语句;
  • constexpr函数不一定返回常量表达式。

(5)、assert(expr);表达式为假(0),输出信息并终止程序,为真什么也不做。
assert定义在cassert头文件中,因为是预处理变量所以不需要声明,它的名字必须唯一

posted @ 2020-11-07 11:36  Z9Y1J5  阅读(128)  评论(0编辑  收藏  举报