异常处理
需要人为throw才能catch,与Java的机制有所区别
throw抛出的异常可以是任意类型
#include <stdexcept> /* * …… */ int num[10] = {0}; try { num[0] = num [1]; throw num[0]; } catch(int err) { cout << "error happend" << endl; } try { throw runtime_error("this is a run time error"); } catch(runtime_error err) { cout << err.what() << endl; }
函数基础
局部静态变量
在程序的执行路径第一次经过对象定义语句时初始化,直到程序终止时才销毁。
int call_count() { static int count = 0; return ++count; } int main() { for(int i = 0; i < 5; i++) { call_count(); } cout << call_count() << endl; //the output is 6 }
使用引用参数传递省去拷贝
const参数
void fun(const int num); //num只能读不能写
可变形参的函数
类型相同,数量不同
void init_list_test(initializer_list<string> str) { for(auto it = str.begin(); it != str.end; it++) cout << *it << " "; cout << endl; } init_list_test({"this", "is", "a", "test"});
省略符形参
#include <stdarg.h> using namespace std; void argFun(int parNum, ...) { va_list ap; char* s; va_start(ap, parNum); for(int i = 0; i < parNum; i++) { s = va_arg(ap, char*); cout << s << endl; } } int main() { argFun(3, "hello", "world", "!"); }
也可以只有省略号,完全忽略形参
尾置返回类型
用于返回类型比较复杂的情况,特别是没有左值的类型
auto fun(int i) -> int (*)[10] //等价于int (*fun(int i))[10] { static int num[10]; for(int j = 0; j < 10; j++) { num[j] = i + j; } return # } int main() { void *result; result = fun(-5); int *temp; for(int i = 0; i < 10; i++) cout << *(temp = (((int*) result) + i)) << endl;
使用decltype,如下函数定义与上述方法等价
int type[10] = {0}; decltype(type) *fun(int i)
函数重载
同一作用域内的几个函数名字相同但形参列表不同,返回值也可以不同
int func(int *ptr); int func(int* const prt); //重复声明了前者 int func(const int *prt); //新函数,作用域指向常量的指针 int func_new(int &num); int func_new(const int &num); //新函数,作用域常量引用
有无顶层const无法被区分,有无底层const可以被区
非常量可以转化为常量,所以比如当仅有3时,传递一个非常量的prt没有错误,但若同时存在1,3,则编译器会优先选择1
const_cast强制转换与重载
如下函数,即省去了函数返回值拷贝的过程,又解决了实参不是const类型时的麻烦
const int &func(const int &i, const int &j); int &func(int &i, int &j); int main() { int m = 1, n = 3; int &result = m; result = func(m ,n); cout << result << endl; } const int &func(const int &i, const int &j) { return (i > j) ? i : j; } int &func(int &i, int &j) { const int &temp = func(const_cast<const int&>(i), const_cast<const int&>(j)); return const_cast<int&>(temp); }
const_cast还可用于多线程下volatile关键字
避免二义性调用
当有多个函数可以匹配时
默认实参
int caculateSquare(int width = 10, int length = 10);
调用时只能省去尾部的实参
内联函数
直接替换,省去开销,一般较小,不支持递归
inline int getMax(int i, int j);
内联函数必须是和函数体申明在一起,才有效。
内联函数和宏很类似,而区别在于,宏是由预处理器对宏进行替代,不检查函数参数,返回值,而内联函数是通过编译器控制来实现的。而且内联函数是真正的函数,只是在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销。你可以象调用函数一样来调用内联函数,而不必担心会产生于处理宏的一些问题。
constexpr
用于常量表达式的函数
返回值及所有形参类型比学位字面值类型,函数体中必须只有一条return语句
可以返回非常量,编译器检查上下文
通常放在头文件中
调试帮助:assert与NDEBUG
assert是一种预处理宏,包含在头文件assert.h中
assert(expr); expr为假时,打印错误,退出;expr为真时,doing nothing
assert的缺点是极大影响程序的性能,产生额外开销
调试结束后可以在#include<assert.h>前加入#define NDEBUG来使所有assert失效
可以利用NDEBUG编写自己的调试代码
#ifndef NDEBUG cerr << "error:NDEBUG test success" ; #endif
预处理器还提供了几个对于调试很有用的局部静态变量
__func__ 当前调试的函数名
__FILE__ 存放当前文件名
__LINE__ 当前行号
__TIME__ 文件编译时间
__DATE__ 文件编译日期
函数指针
声明一个函数指针
bool (*prtf)(double x, int n); //未初始化
可以直接给prtf赋值
int sum(int x, int y) { return x + y; } prtf1 = sum; prtf2 = ∑//等价 prtf1(1,2); (*prtf)(1,2); //等价
重载函数也可以有函数指针
函数指针可以作为形参传入
int prtfPara(int x, int y, int (*prtf) (int m, int n)) { return prtf(x, y) + x; } cout << prtfPara(1, 2, sum) <<endl;
可以用typedef和decltype定义函数的类型,避免冗长和繁琐
typedef int functype1(int x, int y); typedef decltype(sum) *functype2; int prtfPara(int x, int y, functype2 prtf)
返回函数指针的函数
typedef int(*PF)(int x, int y) ; PF pf1(int x) { return sum; } functype1 *func1 = pf1(1); cout << func1(4, 5) << endl;
也可这样声明func1
int (*func2(int m))(int x, int y);
注意:当decltype作用于某个函数时,它返回函数类型而不是指针类型,需要显式的加上*表明是指针类型,例如functype2
最近开的书比较多,上周末又病了一场,节奏有点不太对,有待调整!