一、参数传递
1、在C++中建议使用引用类型的形参替代指针类型的形参。使用引用同时也能避免参数传递时的拷贝,可以提高效率。如果某些类型不支持拷贝操作(如IO类型),函数只能通过引用形参访问该类型对象。如果函数无须改变引用形参的值,最好将其使命为常量引用。
// 比较两个string对象的长度
bool isShorter(const string &s1, const string &s2)
{
return s1.size() < s2.size();
}
2、指针或引用形参与const:形参的初始化方式和变量的初始化方式是一样的。
3、数组形参:
char str[] = "Fuck you!";
// 以下三个函数形式都可以用str数组传参,在函数中都可以使用形参对str数组进行访问
print(const char str[])
print(const char str[10])
print(const char *str)
以上函数在使用形参的时候并不知道实参数组的大小,所以可能引发数组越界。所以产生了以下三种管理指针形参的技术:
- 使用标记指定数组长度
void print(const char *str) { if(str) while(*str) // 只要指针所指的字符不是空字符标记 cout << *str++; cout << endl; } // 以上对于像int类型的数组就不太适用了
- 使用标准库规范:传递数组首元素和尾后元素的指针
int ia[] = {1,2,3,4,5,6}; void print(const int *beg, const int *end) { while(beg != end) cout << *beg++; cout << endl; } // 使用C++ 11标准提供的begin()和end()函数获取数组的首元素指针和尾后指针并作为实参调用函数 print(begin(ia), end(ia));
- 显式传递一个表示数组大小的形参
// const int ia[] 等价于 const int *ia void print(const int ia[], size_t size) { for(size_t i = 0; i != size; ++i) { cout << ia[i]; } cout << endl; } // 调用print print(ia, end(ia) - begin(ia));
函数参数允许定义为数组的引用:
void print(int (&ia)[10]); // 定义时的小括号比不可少,纬度是类型的一部分
int i = 0, j[2] = {0,1}, k[10] = {0,1,2,3,4,5,6,7,8,9};
print(&i); // 错误:数组长度不是10
print(j); // 错误:数组长度不是10
print(k); // 正确
4、传递多维数组参数
// 由于多维数组就是数组的数组,在传递多维数组时应该用以下的方式定义
void print(int (*matrix)[10], int rowSize); // 传递的数组是由10个整数构成的数组
voed print(int matrix[][10], int rowSize); // 与上是等价定义
5、含有可变形参的函数
- 实参类型相同:传递initializer_list的标准库类型(头文件<initializer_list>),提供以下操作:
initializer_list<T> list; // 默认初始化
initializer_list<T> list{a,b,c,d}; // list的元素是对应初始值的副本;列表中的元素是const类型的
list2(list);
list2 = list;
list.size(); // 列表中元素的数量
list.begin(); // 返回list中首元素的指针
list.end(); // 返回指向list中尾元素下一位置的指针 - 省略符形参
- 可变参数模版
二、因为数组不能拷贝赋值,所以函数不能返回数组。不过,函数可以返回数组的指针或引用。定义一个返回数组的指针或引用的函数的方法如下:
typedef int iarrT[10]; // 含有10个整数的数组类型
using iarrT = int[10]; // 与上等价定义
iarrT* function(int i); // 使用类型别名定义
int (*function(int i))[10]; // 这种原始定义太繁琐
auto function(int i) -> int(*)[10]; // C++11中的尾置返回类型
decltype(iarrT) *function(int i); // 使用C++11中的decltype类型推断定义
三、特殊用途语言特性
1、默认实参:一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值。
2、内联函数:内联函数可以避免函数调用的开销。一般来说,内联机制用于优化规模较小、流程直接、频繁调用的函数。很多编译器都不支持内联递归函数,而且一个几十行上百行的代码也不大可能在调用点内联的展开。
constexpr函数:是指能用于常量表达式的函数。约定:函数的参数和返回值都得是字面值类型,而且函数中必须有且只有一条return语句,函数内可以有其他语句,只要这些语句在运行时不执行任何操作就行。constexpr函数返回的不一定是常量表达式。
通常把内联函数和constexpr函数定义在头文件内。
3、assert(expr)。assert宏在头文件cassert中定义。
NDEBUG预处理变量。#define NDEBUG 后,不在运行assert语句。除了用assert以外,还可以结合使用#ifndef NDEBUG、#endif编写自己的条件调试代码。
预处理器定义了另外4个对调试程序很有用的名字:__FILE__、__LINE__、__TIME__、__DATE__。
四、函数指针
bool lengthCompare(const string &str1, const string &str2);
bool (*pf)(const string &, const string &); // *pf两端的括号必不可少
pf = lengthCompare;
pf = &lengthCompare; // 取地址符是可选的
bool b1 = pf("hello", "goodbye");
bool b2 = (*pf)("hello", "goodbye");
bool b3 = lengthCompare("hello", "goodbye");