一、函数基础
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 ",add(val)); //4 printf("%d ",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)、使用typedef和decltype可以简化函数指针;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头文件中,因为是预处理变量所以不需要声明,它的名字必须唯一。