- Sec4 表达式、逻辑和关系运算符
- Sec5 语句
- 5.1 简单语句
- 5.2 语句作用域
- 5.3 条件语句
- 5.4 迭代语句
- 5.6 try语句块和异常处理
- Sec6 函数
- 6.1 函数基础
- 6.2 参数传递
- 6.2.4 数组形参
- 6.2.5 main: 处理命令行选项
- 6.2.6 含有可变形参的函数
- 6.3 返回类型和returen语句
- 6.3.3 返回数组指针
- 6.4 函数重载 (重要)
- 6.5 特殊用途语言特性
- 6.5.3 调试帮助
- 6.6 函数匹配
- 重要!!C/C++类型声明黄金法则
- 6.7 函数指针
- null
Sec4 表达式、逻辑和关系运算符
&&
: 只有左边为真才对右边求值||
: 只有左边为假才对右边求值例子:
【资料图】
index != s.size() && !isspace(s[index])
首先检查index是否达到string对象的末尾,以此才确保只有当index在合理范围之内时,才会计算右侧运算对象的值
递增递减运算符
建议:除非必须,否则不用递增递减运算符的后置版本!!!
常用手法:
cout << *iter++ << endl;
条件运算符
cond ? expr1 : expr2
注意,该运算符的优先级非常低!移位运算符优先级不高不低。比算术运算符低,比关系运算符,赋值、条件运算符高
Sec5 语句
5.1 简单语句
- 空语句
- 分号的使用
- 复合语句compound statement
5.2 语句作用域
- if, switch, while和for语句的控制结构内定义变量。定义在控制结构当中的变量只在相应语句的内部可见
5.3 条件语句
- if-else
- switch记得写break和default别在case里面定义可能会跨case的变量!
5.4 迭代语句
while
for循环
for(init-statement; condition; expression) statement;
流程:先初始化,再判断条件,再执行statement,最后执行expression再判断条件,...,循环往复,直到不满足条件
(C++特性)范围for语句
for(declaration : expression) statement
其中,expression必须为一个序列!(有begin和end成员)declaration定义为一个变量,使得序列中的每个元素都能转换为该变量的类型。(常常用auto,而且引用是一个好习惯,引用后可以对expression中的序列写操作)
- 不能通过范围for语句增加vector对象,因为会存储end()值!!!要是增加就会变得无效了!
do-while语句
跳转语句
break语句
continueyuju
goto语句尽量别用
goto label;
labeled statement
5.6 try语句块和异常处理
C++的异常处理:
- throw:异常检测部分使用throw表达式来表示它遇到了无法处理的问题,我们说throw引发(raise)的异常
- try:异常处理部分使用try语句块处理异常。以try开始,以catch结束。(异常处理代码 exception handler)
- 一套异常类 exception class用于在throw表达式和相关的catch子句之间传递异常的具体信息
throw表达式
if(item1.isbn() != item2.isbn()) throw runtime_error("Data must refer to same ISBN");
try语句块
try{ program-statements} catch (exception-declaration) { handler-statements} catch (exception-declaration) { handler-statement;}
Sec6 函数
6.1 函数基础
- 分离式编译C++开发中广泛使用声明和实现分开的开发形式,其编译过程是分离式编译就是说各个cpp文件完全分开编译,然后生成各自的obj目标文件,最后通过链接器link生成一个可执行的exe文件。不需其他操作。
6.2 参数传递
形参的类型决定了形参和实参的交互方式
- 引用传递(passed by reference) 形参是引用类型引用形参也是它绑定对象的别名
- 值传递(passed by valued)形参和实参是两个独立的对象
- 指针形参注意函数内部可以通过指针的参数来改变外边的值了但只改变指针本身,不会影响外边
==
- 当用实参初始化形参的时候会忽略掉顶层const所以允许用字面值或者非常量,来初始化常量引用(标准是不要改变常量)
尽量使用常量引用
6.2.4 数组形参
管理方法
- 使用标记指定数组长度
- 使用标准库规范begin,end
- 显式传递一个表示数组大小的形参
数组形参和const当函数不需要对数组元素执行写操作,数组形参应该是指向const的指针
数组引用形参
f(int &arr[10]);// 错误,不存在引用的数组f(int (&arr)[10]);// 正确,arr是一个具有10个整数的整型数组的引用
多维数组
int *matrix[10];// 10个指针构成的数组int (*matrix)[10];// 指向含有10个整数的数组的指针
6.2.5 main: 处理命令行选项
int main(int argc, char *argv[])
argv为一个数组,元素是指向c风格字符串的指针。第一个形参为argc,是表示数组中字符串的数量!
命令: prog -d -o ofile data0argv[0]="prog";// 第一个元素指向程序的名字或者一个空字符串argv[1]="-d";// 饥饿下来的元素以此传递命令行提供的参数argv[2]="-o";argv[3]="ofile";argv[4]="data0";argv[5]=0;// 最后一个指针之后的元素保证为0
6.2.6 含有可变形参的函数
编写能处理不同数量实参的函数:
- 如果所有的实参类型相同,可以传递一个名为initializer_list的标准库类型
- 如果实参的类型不同,可以编写一种特殊的函数。即可变参数模板
initializer_list
一种标准库类型,用于表示某种特定类型的值的数组initializer_list
lst;// 默认初始化,T类型元素的空列表initializer_list lst{a,b,c...}// lst的元素数量和初始值一样多,lst的元素是对应初始值的副本,列表中迭代元素是constlst2(lst);// 拷贝或者赋值不回拷贝列表中的元素,而是原始列表和副本共享元素lst2 = lst;lst.size();lst.begin();lst.end(); initializer_list
永远是常量值省略符形参仅仅用于C于C++通用的类型!
void foo(parm_list,...); // 省略符之后的形参不会执行类型检查void foo(...);
6.3 返回类型和returen语句
返回
- 无返回值函数
- 有返回值函数
如何返回值返回的值用于初始化调用点的一个临时变量
返回的注意事项不要返回局部对象的引用或指针因为函数完成后,所占用的存储空间也随之被释放掉
const string &manip(){ string ret; if(!ret.empty()) return ret;// 错误,返回局部对象的引用 else return "empty";// 错误,empty是一个局部临时量}
返回类类型的函数和调用运算符调用运算符的优先级和点和箭头运算符相等。且符合左结合律
引用返回左值调用一个返回引用的函数得到左值,其他返回类型为右值
// 例子char &get_val(string &str, string::size_type ix){ return str[ix];}int main(){ string s("a value"); cout << s << endl; get_val(s, 0) = "A"; cout << s << endl; return 0;}
列表初始化返回值很好用。返回一个{}起来的值,然后用里面的值来初始化!
main的返回值
return EXIT_FAILURE;return EXIT_SUCCESS;
6.3.3 返回数组指针
函数可以返回数组的指针或者引用。
typedef int arrT[10];using arrT = int[10];arrT* func(int i);// func返回一个指向含有10个整数的数组的指针
int arr[10];int *p1[10];int (*p2)[10] = &arr;
或者直接定义
type (*function(parameter_list))[dimension]// 返回数组指针的函数
使用尾置返回类型trailing return type对返回值比较复杂的函数最有效
auto func(int i) -> int(*)[10];// 把函数返回类型放在->之后,并在前面用auto定义
或者用decltype
6.4 函数重载 (重要)
pass
6.5 特殊用途语言特性
三种函数相关的语言特性
默认实参一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值只能省略尾部的实参!!!
window = screen(,,"?");// 错误
而且给定的作用域中,一个形参只能被赋予一次默认实参只要表达式的类型可以转换为对应类型,该表达式就可以作为默认实参
内联函数
constexper
内联函数可以避免函数调用的开销
inline
constexper函数指能用于常量表达式(编译过程就能得到结果)的函数函数返回类型以及所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条return语句会被隐式的展开为内联函数
建议把内联函数和constexper函数放在头文件里面
6.5.3 调试帮助
头文件保护
assert预处理宏preprocessor marco
assert(expr);
先对expr求值,如果expr为0,则输出信息并终止程序执行。如果为真,则啥也不做
NDEBUG预处理变量如果定义了 NDEBUG,则assert什么也不做。默认状态下没有定义NDEBUG,此时assert将执行运行时检查
// 对调试有用的名字__func____FILE____TIME____LINE____DATE__
6.6 函数匹配
void f();void f(int);void f(int, int);void f(double, double = 3.14);
确定候选函数和可行函数candidate function / viable function
寻找最佳匹配基本思想是,实参类型与形参类型越接近,匹配得越好
含有多个形参的函数匹配例如:
f(42,2.56)
可行函数为f(int,int), f(double, double)
确定最佳匹配的条件
- 该函数每个实参的匹配都不劣于其他可行函数所需要的匹配
- 至少有一个实参的匹配优于其他可行函数提供的匹配
故该调用具有二义性,拒绝并报错
实参类型转换
重要!!C/C++类型声明黄金法则
- 步骤:
- 找到变量名,若无变量名,则找到最里边的结构
- 向右看,读出你看到的东西,但是不要跳过括号!
- 再向左看,读出你看到的东西,但也不要跳过括号!
- 如果有括号,跳出一层括号
- 重复上述过程,直到读出最终类型
6.7 函数指针
// 比较两个string对象的长度bool lengthCompare(const string &, const string &);// pf指向一个函数,该函数的参数是两个const string的引用,返回值是bool类型bool (*pf)(const string &, const string &); // 未初始化
一定要写括号!如果不写的话,返回值就是一个指向bool类型的指针
使用函数指针当我们把函数名作为一个值使用的时候,该函数自动地转换成指针。
pf = func;pf = &func;// &是可选的。这两条是等价的,而且func应该是bool类型的!返回类型要一致// 调用bool b1 = pf("hello", "good");bool b2 = (*pf)("hello", "good");// 这两条也是等价的
重载函数的指针通过指针类型决定选用哪个函数。指针类型必须与重载函数中的某一个精确匹配
函数指针形参
void useBigger(const string &s1, const string &s2, bool pf(const string &, const string &))
可以用decltype和typedef来简化操作!
// Func和Func2是函数类型typedef bool Func(const string&, const string&);typedef decltype(lengthCompare) Func2;// 等价的类型// FuncP和FuncP2是指向函数的指针typedef bool (*FuncP)(const string&, const string&);typedef decltype(lengthCompare) *FuncP2;// 等价声明void useBigger(const string&, const string&, Func);// 自动将Func转换为指针void useBigger(const string&, const string&, FuncP2);// 等价
返回指向函数的指针|注意,不能返回一个函数!
using F = int(int*, int);// F为函数类型using PF = int(*)(int*, int);// PF为指针类型
PF f1(int);// 正确F f1(int);// 错误F *f1(int);// 正确,与第一条等价,返回为指向函数的指针auto f1(int) -> int (*)(int*, int);// 尾置返回类型