1.函数内的局部静态对象在程序的执行路径第一次经过对象定义语句的时候初始化,并且直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响。
size_t get_count() { static size_t count = 0; return ++count; }
2.当实参初始化形参的时候会忽略顶层const,换句话说,当形参有顶层const时,传给它常量或者非常量都是可以的。
const int ci = 1; // ci的值不能被改变,const是顶层的 int i = ci; // 当拷贝ci时, 忽略了它的顶层const void fun(const int i){} void fun(int i){} // 错误,重复定义
3.数组在做形参的时候是不允许拷贝数组的,而且数组通常会被转换为指针。
// 以下三个声明是等价的,编译器只会检查参数是否为int* void printArr(int *arr); void printArr(int arr[]); void printArr(int arr[10]);
4.如果想固定数组的大小,可以使用引用形参。
void printArr(int(&arr)[10]) {} int arr[1] = { 0 }; printArr(arr); // 错误,arr不是长度为10的数组
5.如果函数的实参数量未知,但类型都相同,可以使用 initializer_list 类型的形参,initializer_list是一种标准库类型,可以表示某种特定类型的值的数组。它和 vector 一样都是模板类型,但是不可以修改它的元素。
initializer_list<int> lt = { 1, 2, 3 };
6.省略符形参应该仅仅用于C和C++通用的类型,大多数类类型的对象在传递给省略符形参的时候无法正确的被拷贝。而且省略符形参只能出现在形参列表的最后一个位置。
void fun(...) {} void fun1(int, ...) {} // 其中的逗号可以省略 fun1(1, "test", "test2");
7.如果函数的返回类型不是void那么必须返回一个值,但是main函数可以例外,允许main函数没有return也可以正常结束,编译器将隐式的为main函数加入一个返回0的return语句。
8.main函数不可以递归,也不可以重载。
9.因为数组不能拷贝,所以函数不能返回数组,不过函数可以返回数组的指针或引用。
// 使用别名的方式 typedef int arrT[10]; using arrT = int[10]; arrT* func(int i);
// 不使用别名的方式 int (*func(int i))[10];
// 尾置返回类型的方式(C++ 11) auto func(int i) -> int(*)[10];
// decltype 的方式(C++ 11) int arr[10]; decltype(arr) *func(int i);
10.如果在函数内层作用域中声明名字,它将隐藏外层作用域中声明的同名实体。即在不同的作用域中无法重载函数名。
std::string read(); void print(const string&); void print(double); void Test() { bool read = false; // 新作用域,隐藏了外层的read std::string s = read(); // 错误,read是个bool值 void print(int); // 新作用域,隐藏了外层的print print("test"); // 错误,print(const string&)已被隐藏 print(1); // 正确 print(3.14); // 正确,调用的是print(int) }
11.constexper函数是指能用于常量表达式的函数,要遵循两个规定,函数的返回类型及所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条return语句
constexpr int count() { return 1; } constexpr int i = count();
12.允许constexpr函数的返回值并非一个常量
constexpr int count() { return 1; } constexpr int total_count(std::size_t num) { return num * count(); };
但是上述的参数num如果给的不是一个常量,则编译器会发出错误信息。
13.和其他函数不一样,内联函数和constexpr函数可以在程序中多次定义,但是它的多个定义必须完全一致,基于这个原因,内联函数和constexpr函数通常定义在头文件中。
14.assert的行为依赖于一个名为NDEBUG的预处理变量的状态,如果定义了NDEBUG,则assert什么也不做。
15.当编译器无法找出最佳匹配的函数时会产生二义性的错误,我们可以使用强制类型转换来避免,但这不是一个好的选择。
void pritf(int, int); void pritf(double, double); pritf(1, 3.14); // 编译器不知道该匹配哪个函数,产生二义性错误
16.函数指针指向的是函数而非对象。
int get_max(int a, int b); int (*pf)(int, int); // pf指向一个函数,该函数返回值是int(如果*pf不加括号,函数的返回值就是int*) // 以下两个初始化等价 pf = get_max; pf = &get_max; //以下两个调用等价 int i = pf(1, 2); int j = (*pf)(1, 2);
17.和数组类似,虽然不能定义函数类型的形参,但是形参可以是指向函数的指针。
// 以下两个声明等价 void use_max(int a, int b, int pf(int,int)); void use_max(int a, int b, int (*pf)(int, int)); // 我们可以直接把函数作为实参使用,此时它会自定转换成指针 use_max(1, 2, get_max);
18.和数组类似,虽然不能返回一个函数,但是能返回指向函数类型的指针。
int get_max(int a, int b); // 使用别名 using F = int(int, int); using pF = int(*)(int, int); pF f1(int); // 正确 F f2(int); // 错误,返回类型不会自动地转换成指针 F *f3(int); // 正确 // 当然我们也能不用别名的形式直接声明 int (*f4(int))(int, int); // 使用auto或者decltype auto f5(int) -> int(*)(int, int); decltype(get_max) *f6(int, int);