两个非常重要的概念:
局部变量(local variable):形参和函数体内部定义的变量统称为局部变量。它对函数而言是局部的,对函数外部而言是隐藏的。它的生命周期依赖于定义的方式。
在所有函数体之外定义的对象存在于程序的整个执行过程中。此类对象在程序启动时被创建,直到程序结束才会被销毁。
自动对象:只存在于块执行期间的对象。当块的执行结束后,它的值就变成未定义的了。
局部静态对象: static类型的局部变量,在程序的执行路径第一次经过对象定义语句时被创建,直到程序终止才被销毁。它的生命周期贯穿函数调用前后。
分离式编译: CC a.cc b.cc直接编译生成可执行文件;CC -c a.cc b.cc编译生成对象代码a.o b.o; CC a.o b.o编译生成可执行文件。
形参初始化的机理和变量初始化一样。
引用传递(passed by reference):又称传引用调用(called by reference),指形参是引用类型,引用形参是它对应的实参的别名。
值传递(passed by value):又称传值调用(called by value),指实参的值是通过拷贝传递给形参。
形参的顶层const被忽略。void func(const int i); 调用时既可以传入const int也可以传入int。
由于这个特性,所以void func(const int i); 和 void func(int i); 不是重载。
可以使用非常量初始化一个底层const对象,但是反过来不行。
尽量使用常量引用。
两个特殊性质:
以数组作为新参的函数,也要注意数组的实际长度,不能越界。
管理数组实参的第一种方法:要求数组本身包含一个结束标记。
void print(const char cp) {
if(cp){
while(cp) cout << *cp++;
}
}
管理数组实参的第二种方法:使用标注库规范,传递指向数组首元素和尾元素的指针。推荐使用。
void print(const int *beg, const int *en) {
while(beg != en) cout << *beg++;
}
int j[2] = {0, 1, 2423, 4};
print(begin(j), end(j));
管理数组实参的第三种方法:专门定义一个表示数组大小的形参。
// const in ia[] <=> const in *ia
void print(const in ia[], size_t size) {
for (size_t i = 0; i < size; ++i) {
cout << ia[i];
}
}
传递多维数组
void print(int (*matrix)[10], int rowSize) {
;
}
// 或者
void print(int matrix[] [10], int rowSize) {
;
}
C++11提供了两种主要的方法来解决处理不同数量实参的函数
initializer_list形参:initializer_list是一种标准库类型,用于表示某种特定的值的数组,它定义在同名的头文件中。它的操作如下表:
操作
含义
initializer_list lst;
默认初始化;T类型元素的空列表。
initializer_list lst{a,b,c};
lst的元素数量和初始值一样多;lst的元素是对应初始值的副本;列表中的元素是const。
lst2(lst)
拷贝或赋值一个initializer_list对象不会拷贝列表中的元素;拷贝后,原始列表和副本共享元素。
lst2 = lst
原始列表和副本共享元素
lst.size()
列表中的元素数量
lst.begin()
返回指向lst中首元素的指针
lst.end()
返回指向lst中微元素下一位置的指针
void err_msg(ErrCode e, initializer_list<string> il){
cout << e.msg() << endl;
for(cosnt auto$elem: il)
cout << elem << " ";
cout << endl;
}
if (expected != actual)
err_msg(ErrCode(42), {"funcX", expected, aactual};
else
err_msg(ErrCode(0), {"funcX", "Okay"};
type(*function(parameter_list))[dimension]
使用类型别名:typedef int arrT[10]; 或者 using arrT = int[10;],然后 arrT* func() {…};
使用decltype:decltype(odd) *arrPtr(int i) {…};
C++11:尾置返回类型:在形参列表后面以一个->开始:auto func(int i) -> int(*)[10]
重载:如果同一作用域内几个函数名字相同但形参列表不同,我们称之为重载(overload)函数。
main函数不能重载。
重载和const形参
const_cast和重载
// 比较两个string对象的长度,返回较短的那个引用
const string &shorterString(const string &s1, const string &s2) {
return s1.size() <= s2.size() ? s1 : s2;
}
// 重载一个版本:实参不是常量时,得到一个普通的引用。
string &shorterString(string &s1, string &s2) {
auto &r = shorterString(const_cast
return const_cast
}
重载和作用域:若在内层作用域中声明名字,它将隐藏外层作用域中声明的同名实体,在不同的作用域中无法重载函数名。
普通函数的一个潜在缺点:调用函数一般比求等价表达式的值要慢一些。因为调用前要先保存寄存器,并在返回时恢复;还可能需要拷贝实参;程序转向一个新的位置继续执行等;
inline函数可以避免函数调用的开销,可以让编译器在编译时内联地展开该函数。但是注意,内联说明只是向编译器发出一个请求,编译器可以选择忽略这个请求。
inline const string & shorterString(const string &s1, const string &s2) {
return s1.size() <= s2.size() ? s1 : s2;
}
// 使用
cout << shorterString(S1, S2) << endl;
// 编译过程中直接展开为类似下面的形式
cout << (s1.size() <= s2.size() ? s1 : s2) << endl;
inline函数应该在头文件中定义。且只适合短小的函数。
assert预处理宏(preprocessor macro):assert(expr);
NEDBUG预处理变量:CC -D NDEBUG main.c可以定义这个变量NDEBUG。
void print(){
#ifndef NDEBUG
cerr << func << "…" << endl;
#endif
}
重载函数匹配的三个步骤:1.候选函数;2.可行函数;3.寻找最佳匹配。
候选函数:选定本次调用对应的重载函数集,集合中的函数称为候选函数(candidate function)。
可行函数:考察本次调用提供的实参,选出可以被这组实参调用的函数,新选出的函数称为可行函数(viable function)。
寻找最佳匹配:基本思想:实参类型和形参类型越接近,它们匹配地越好。
函数指针指向的是函数而非对象,它指向某种特定类型。函数的类型由它的返回类型和形参类型共同决定,与函数名无关。
// 比较两个string对象的长度
bool lengthCompare(const string& a, const string& b);
// 该函数的类型
bool (const string&, const string&);
// 变成函数指针,只需要将指针替换函数名即可
bool (*pr) (const string&, const string&);
使用函数指针:当把函数名作为一个值使用时,该函数自动转换成指针。
pf = lengthCompare; // pf指向名为lengthCompare的函数
pf = &lengthCompare; // 等价的赋值语句:取地址符是可选的
函数指针形参:
返回指向函数的指针:1.类型别名;2.尾置返回类型。
手机扫一扫
移动阅读更方便
你可能感兴趣的文章