C++学习笔记-函数

局部变量:

形参和函数体内定义的变量

局部变量又分为自动变量和局部静态变量

注意,局部静态变量在程序的执行路径第一次经过对象定义语句时初始化,直到程序终止才被销毁。所以一个简单的应用是,用局部静态变量统计函数被调用 的次数。

函数声明

最好在函数声明中写上形参的名字。把函数声明放在头文件中

指针形参

指针的行为与其他非引用类型一样。对执行指针拷贝操作时,拷贝的是指针的指,所以不能修改传入的实参指针。但是可以修改实参指针所指向的对象的值

所以对于函数f(int *p){*p=3;p=0;}

如果int a=5;

int *q=&a;

f(&a);

f(q);

cout<<a;   //输出3

cout<<q;  //q不变,仍是a的地址

引用传递

引用有几种用途:

1.想改变传入的实参的值

2.避免拷贝大的类或容器对象

3.可传递某些根本不支持拷贝的类对象作为实参

4.使函数能够返回多个值

尽量使用常量引用

1.明确给调用者传递信息:该函数不能修改实参的值

2.能接受更多类型的实参:非常量对象,字面值,或者通过类型转换能转换成形参类型的对象(参数传递规则与初始化规则是一样的)(普通的引用形参不支持传递这几种类型的实参)

数组形参

形参是数组的函数,以下三种形式是等价的

f(const int*);

f(const int[]);

f(const int[10]);

上面每个函数的唯一形参都是const int*,因此实参可以是任意大小的整型数组,还可以是整型变量

但是只是像上面那样定义形参是数组的函数,函数内部就没有足够的信息确定数组的大小,因此,

定义形参是数组的函数有如下三种形式:

1.如果知道指针指向的数组何时结束,可将函数定义成 f(const char *p){}(传入C风格的字符串,则*p=='\0'表示数组的结束)

2.使用两个指针,分别表示数组的开始和结束    f(const in *beg,const int *end){}

3.常用的一种方法,用一个参数指出数组的大小    f(const int *p,size_t n){}

如果不需要改变数组的内容,那么应该将数组形参定义成const变量

数组引用形参

f(const int (&arr)[10]){}的形参是具有10个整数的数组的引用,注意此时传入的实参必须是含有10个整数的数组

多维数组形参

首先,要明白,多维数组其实是数组的数组

其次,指向多维数组的指针实际上是指向数组首元素的指针

定义形参是多维数组的函数

f(int (*matrix)[10],int rowSize){}  注意,数组的第二维(及后面的所有维数)的大小不能省略,参数rowSize指定数组的行数

f(int matrix[][10],int rowSize){}  注意,数组的第二维(及后面的所有维数)的大小不能省略,参数rowSize指定数组的行数

含有可变形参的函数

1.使用类模板initializer_list 。

函数的定义;f(initializer_list<int> il){}

函数的调用:f({a,b,c});其中a,b,c是该类型的常量或者变量。注意,大括号不能少

2.使用省略符

函数的定义 f(int a,...){}

省略符形参应该只用于与C函数交互的接口程序。特别应该注意的是,大多数类对象作为实参传入时,都不能正确拷贝

函数的返回值

1.对于返回值是void类型的函数,如果要提前终止程序,用return;

2.函数返回一个值,那么返回的值初始化了调用点的一个临时量

3.函数返回引用,那么不会发生拷贝。返回的引用只是所引对象的别名

4.不要返回局部对象的引用或指针(函数结束后,引用或指针指向了不可用的内存空间)

5.若函数返回引用(非常量引用),则函数调用可用作左值

6.C++11新增,函数可以返回用花括号包围起来的列表。该列表对函数返回的临时量进行初始化。初始化规则取决于函数返回值类型的初始化规则

7.函数返回数组指针(即指向数组的指针)。该类函数的定义形式有如下几种:

  • int(*f(int i))[10]{}  ,这种方法与定义数组指针非常类似
  • 使用类型别名   typedef int arrT[10];(using arrT=int[10];)arrT *f(int i){}  //这种方式很方便
  • 使用尾置返回类型  auto f(int i)->int(*)[10]{}
  • 使用decltype    int a[10];decltype(a) *f(int i){}
重载函数

若两个函数的形参只是顶层const和顶层非const的区别,那个这两个函数是等价的,不构成重载

若形参的const属性不同,且是底层const,则这两个函数是重载函数。例 f(const int &a)与f(int &a)    f(const int *a)和f(int *a)

const_cast可用来增加或者去除对象的const属性,在重载函数中的情景中最有用,用来在一个const(或者非const)函数中调用另一个非const(或者const)函数

如果在内层作用域中声明名字,它将隐藏外层作用域中声明的同名实体(变量或函数)

size_t和size_type

size_t是数组下标的输入参数的类型,在cstddef头文件中定义

string::size_type是string对象的size()函数的返回值的类型,也是string对象 的下标的输入参数的类型

vector<T>::size_type是vector对象的size()函数的返回值的类型,也是vector对象 的下标的输入参数的类型

size_t和size_type都是无符号类型,长度与机器相关

默认实参

1.局部变量不能作为默认实参

2.只要表达式的类型能转换成开通所需的类型,该表达式就能作为 默认实参

3.用作默认实参的名字在函数声明所在的作用域内解析

内联函数

通常将规模较小的操作定义成内联函数。这样整个代码清晰,函数便于重复使用,日后修改起来也方便

constexpr函数

1.函数的返回类型和形参的类型都必须是字面值类型(算术类型,引用,指针,不能是类类型)

2.函数体有且仅有一条return语句.若函数体内包含其它语句,这些语句在运行时不能执行任何操作(比如,可以有类型别名和using声明)

3.constexpr函数不一定返回常量表达式

4.constexpr函数可以作为constexpr变量的初始值,而普通函数不能。constexpr函数作为constexpr变量的初始值时,函数的返回值必须是常量表达式,否则编译器会报错。

5.constexpr函数被隐式地指定为内联函数

6.constexpr函数用于初始化constexpr类型的变量。初始化时,编译器把对constexpr函数的调用替换成其结果值


程序可以包含一些用于调试的代码,这些代码只在开发程序时使用。发布程序时,屏蔽掉这些代码。

使用预处理宏assert

1.调试时,程序中加入assert(expr).expr为假时,assert输出信息并终止程序的运行。(输出的信息内容不用自己指定)。expr为真时,assert什么也不做。

2.使用assert需要在程序中加入头文件cassert。

3.程序发布时,在#include <cassert>前面加上#define NDEBUG,这样就不会执行运行时检查,即assert什么也不做

4.assert应该仅用于验证那些确实不可能发生的事。assert不能代替真正的运行时逻辑检查和其他的错误检查(发布程序时,assert被屏蔽掉,不能起到检查的作用)

使用预处理器变量NDEBUG

1.调试时,可在程序的任意地方加入下面代码

#ifndef NDEBUG

.......

#endif

2.发布程序前,在程序的头部加上#define NDEBUG,就能忽略掉上面的调试代码

重载函数的匹配步骤

1.选候选函数。候选函数要与被调用的函数同名,且其声明可见

2.选可行函数。从候选函数中以以下规则选出可行函数:形参数量与实参数量相等,且形参类型与实参类型相同或形参类型是实参可转换的类型

3.找最佳匹配函数。从可行函数中找形参类型与实参类型最匹配的函数。匹配优先级从高到低:

  • 精确匹配
  • 通过const转换实现的匹配
  • 通过类型提升实现的匹配(简言之,int以下的提升到int,或者unsinged int)
  • 通过算术类型转换(int以下的先提升到int,若另一运算对象更宽,则int再转换到另一运算对象的类型;注意,所有算术类型转换的级别一样)或者指针转换(数组名转换为指针;非0的指针转换为true)实现的匹配
  • 通过类类型转换实现的匹配

函数指针

定义函数指针:用指针代替函数名  bool (*pfn)(const int ,const int);  

给函数指针赋值

pfn=0;//pfn不指向任何函数

bool f(const int,const int);

pfn=f;//fpn指向f

注意f必须与p的形参类型和返回值类型精确匹配。f是重载函数时,pfn指向精确匹配的那个函数

定义函数指针并初始化

bool (*pfn)(const int ,const int)=f;(函数名自动转换成指针)

函数作为形参

定义函数类型别名

bool f(int ,int);

1.typedef bool f1(int ,int);

2.typedef decltype(f) f2;

3.typedef bool (*pf1)(int ,int);

4.typedef decltype(f) *pf2;

其中f1,f2都是函数,pf1,pf2都是指向函数的指针(decltype(f)的结果是函数类型)


定义函数作为形参的函数:

1.void ff(int ,int ,bool f(int,int));

2.void ff(int ,int ,boo (*pf)(int,int));

上面的两种形式是等价的。1第三个形参看起来是函数,实际上是指向函数的指针。这与数组当作形参类似

3.用类型别名定义函数作为形参的函数

void ff(int ,int,f1)

或者void ff(int ,int,f2)

或者void ff(int ,int,pf1)

或者void ff(int ,int,pf2)

调用函数作为形参的函数

int a=2;

int b=3;

ff(a,b,f);(注意第三个参数不要写成f(a,b)这种函数调用的形式)

返回指向函数的指针

同数组类似,(函数不能返回数组),虽然函数不能返回函数,但是可以返回指向函数类型的指针。

1.显式声明

int (*f1(int))(int ,int);//f1是函数,它返回一个指向函数类型的指针int(*)(int,int);

2.使用类型别名

using F=int (int ,int);//F是函数类型

using PF=int(*)(int,int);//PF是指向函数类型的指针

PF f1(int);

F *f1int);

(注意,若写成F f1(int);则f返回的是函数,而函数是不能返回函数的,所以这种写法不正确)

3.使用auto

auto f1()->int(*)(int,int);

4.使用decltype

int f(int,int);

decltype(f) *f1(int);(注意,decltype(f)的结果是函数)



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值