函数是一个命名了的代码块,我们通过调用函数执行相应的代码。函数有0个或多个参数,而且(通常)会产生一个结果。可以重载函数,也就是说,同一个名字可以对应几个不同的函数。
函数定义的源文件应把函数声明的头文件包含进来,编译器负责验证函数定义和声明是否匹配。p186
(相关资料图)
形参初始化的机理和变量初始化一样。p187
指针形参拷贝的是指针的值,两个指针是不同的指针,但指向相同的对象。p188
拷贝大的类类型对象或容器对象比较低效,有些类类型根本不支持拷贝操作,函数可通过引用形参访问该类型对象。p189
const
可以表示任意对象是常量,而底层const
与引用和指针等符合类型的基本类型部分有关。p571int x = 0;2const int *const p = &x //左边是底层const右边是顶层const3const int &r = x //声明引用的都是底层const
在执行对象的拷贝操作时顶层const
不受什么影响。p58对于底层const
的对象必须固有相同的底层const
资格,或者两个对象的数据类型必须能够相互转换。p58可以用非常量去初始化一个底层const
对象,但不能用底层const
对象去初始化一个非常量。p191将不会改变的形参定义为常量引用,扩大了函数所能接受实参类型。p192
1//这三个print是等价的,调用时只检查参数是否是const int*, 所以传入的是一个int的指针也正确2void print(const int*);3void print(const int[]);4void print(const int[10]); //10表示期望数组元素个数,与实际传入数组的元素个数无关5
获得数组尺寸的三种常用技术:p193使用标记指定数组长度,如c风格字符串。使用标准库规范,传入数组首指针和尾后指针。
1void print(const int *beg, const int *end)2{3 while(beg != end)4 {5 /*...*/6 }7}
显示传递一个表示数组大小的形参。当函数不需要对数组元素修改时,数组形参应该是指向const
的指针。p194数组本身是对象所以允许定义数组的指针以及数组的引用。p102
1int *ptrs[10]; //包含10个指针的数组2int &ref[10]; //错误,不存在引用的数组3int (*Parray)[10] = &arr; //指向一个含有10个整数数组的指针,&不能少4int (&arrRef)[10] = arr; //引用一个含有10个整数数组的引用, 此时的数组名不再是一个指针
多维数组是数组的数组,多维数组的首元素本身就是一个数组,所以多维数组传递的是一个指向数组的指针。数组第二维(以及后面所有维度)的大小都是数组类型的一部分,不能省略。
1void print(int (*matrix)[10], int rowSsize); //matrix指向数组的首元素,一个10个整数构成的数组2void print(int matrix[][10], int rowSize); //等价定义,编译器同样会忽略掉第一个维度
如果所有的实参类型相同,可以转换成一个名为initializer_list
的标准库类型。p197
拷贝或者赋值一个initializer_list
对象不会拷贝列表中的元素;拷贝后,原始列表与副本共享元素。p198
//验证方法:initializer_list<int> l1 = { 1, 2, 3, 4 };initializer_list<int> l2(l1);cout << l1.begin() << endl;cout << l2.begin() << endl; //结果输出地址相同
initializer_list
对象中的元素永远是常量值,我们无法改变其中元素的值。p198
1int sum(initializer_list<int> li)2{3 int res = 0;4 for(const int &i : li) //引用防止拷贝,但必须加const或者直接用auto&5 {6 res += i;7 }8 return res;9}
1sum({1, 2, 3, 4}); //调用
1shorterString("hi", "bye").size();
调用一个返回类型引用的函数得到左值,其他返回类型得到右值,可以为返回类型是==非常量==引用的函数的结果赋值。p202
1int& func(int& x) 2{ 3 return x; 4} 5 6int main() 7{ 8 int x = 10; 9 func(x) = 20;10 cout << x << endl; //x被修改为了201112 system("pause");13}
函数可以返回花括号包围的值的列表。p203
因为数组不能拷贝,所以只能返回数组的指针或者引用,有四种方法:
直接定义1int (&func(int i))[10];
利用类型别名
1//两种声明等价2typedef int arrT[10];3using arrT = int[10];45arrT& func(int i);
利用尾置返回类型
1auto func(int i) -> int(&) [10]2
使用decltype
1int arr[10];2decltype(arr)& func(int i);
const
的形参无法和另一个没有顶层const
的形参区分开来。p2081//例子2void func(int* p);3void func(int* const p);
底层const
可以实现函数重载
1void func(int* p);2void func(const int* p);
1int func(int x = 10, int y = 20, int z = 30); //声明
1//调用2func(, , 30); //错误
函数多次声明,后续的声明只能为之前没有默认值的形参增加默认实参,而且形参右侧参数必须都有默认值。p212
1int func(int x, int y, int z = 20); //第一次声明2int func(int x, int y = 30, int z); //第二次声明,此时y默认值为30,z默认值为20
函数声明中有了默认值,函数定义中不能有默认值。局部变量不能作为默认实参, 此外的表达式类型只要能够转换成形参所需类型就可以作为默认实参。
cosntexpr
函数的返回值类型以及所有形参的类型都得是字面值类型,且有且仅有一条return
语句。p2141//只有实参是常量表达式时,返回值才是常量表达式2constexpr int func(int x)3{4 return x * 10;5}
内联函数和constexpr
函数的定义应该在头文件内,因为编译器要在调用点内联展开只有函数原型是不够的,同时确保所有源文件的定义完全相同。p215
1assert(expr) //expr为假时输出信息并终止程序
如果定义了NUEBUG预处理变量,则assert什么也不做。p216
函数匹配是从可行函数中选择与本次调用最匹配的函数,实参类型与形参类型越接近,匹配效果越好。p218
1//声明2int func(int, int);3int func(double, double);4
1//调用2func(3, 3.14) //无法判断孰优孰劣,产生二义性报错
1void swap(int& a, int& b);2void (*pf)(int&, int&); //函数指针
当把函数名作为一个值使用时,它会自动转换成指针。p221可以直接用指针名调用函数,无需解引用。p221
1int x = 10, y = 20;2//三种调用等价3pf(x, y);4(*pf)(x, y);5swap(x, y);
不能定义函数类型的形参,但形参可以是指向函数的指针。p222
1//多次swap2void func(int n, int& x, int& y, void (*pf)(int&, int&))3void func(int n, int& x, int& y, void pf(int&, int&)) //等价定义,函数类型自动转换成函数的指针4
1func(3, x, y, swap) //调用,函数名自动转换成函数指针
可用类型申明和decltype
简化类型名。
1typedef decltype(swap) *swapP;
和数组类似,不能返回一个函数,但可以返回指向数组的指针,和函数类型形参不同,返回类型不会自动地转换成指针。p223
标签:
X 关闭
X 关闭