基础语法
constexpr
C++11引入
编译时常量,编译时直接计算结果,不必把表达式带入运行时计算,所以满足条件:编译时可计算
1 | constexpr int get_five() { return 5; } |
运算符优先级
- 作用域运算符与括号(
::
,->
,.
) - 一元运算符 (
++
,--
,*
,&
) - 算数运算符
- 关系运算符(
>
,<
,==
,!=
) - 位运算符
- 逻辑运算符
- 赋值运算符
结合方向:多数运算符都是自左向右,自右向左的有:一元运算符、条件运算符、以及赋值运算符
赋值运算符的返回值
即为所附的值
1 | if((a=3) == 3){} //true; |
函数重载匹配的顺序
1.实参形参类型相同、数组类型和指针类型之间的转换
2.const转换实现的匹配。
3.通过类型提升实现的匹配。
4.算数类型转换或者指针转换实现的匹配。
5.类类型转换实现的匹配
返回参数尾置返回类型
C++11引入
传统方式:int (*func(int i))[10]
表示返回一个指针,该指针指向含有十个整数的数组
返回参数尾置:auto func(int i)->int(*)[10]
命名空间别名
1 | namespace Long = AVeryVeryLongName::AnotherVeryVeryLongNameForTest; |
匿名命名空间
匿名命名空间的内容只能在本文件使用,和static的作用相似,但是看起来更加简洁
1 | namespace { // 没有名字 |
浮点数转整数
直接去掉小数点后面的数值,9.99 = int 9,9.34 = int 9
数值溢出,可能出现未定义的行为
面向对象特性
explicit、default、delete
default:要求编译器生成默认构造函数
explicit:拒绝对象被隐式构造(单参数的构造函数尤其需要注意),不会被继承
delete:删除默认构造函数避免对象不符合预期的被构造
1 | class MyClass |
类成员访问限制
友元关系不能被继承
继承方式与基类成员访问权限,如下
基类成员访问属性 | 在派生类中的访问属性 | ||
---|---|---|---|
public | protected | private | |
public | public | protected | private |
protected | protected | protected | private |
private | inaccessible | inaccessible | inaccessible |
override、final
- override:重写基类虚函数,编译时检查
- final:到此为止,子类不能重写此虚函数
析构函数的虚化目的
派生类通过基类指针释放,若无虚化,这调用基类析构函数,若虚化,则先调派生类,再调基类
继承但没有虚函数
dynamic_cast无法通过编译
typeid运算符会给出静态绑定结果
typeid
用途:获取类型,常用于类型比较,当用于多态类型的表达式时,运行时进行虚表查找,其他情况下 typeid 表达式都在编译时解决。
typeid(T).name()
:返回T的类型名称,若存在多态,将返回真实类型(派生类)
typeid(i) == typeid(int)
:类型比较,用于泛型与多态
类型转换
xxx_cast <new_type> (expression)
目的
克服C语言强制类型转换的以下三个缺点
没有从形式上体现转换功能和风险的不同
将多态基类指针转换成派生类指针时不检查安全性(dynamic_cast)
- 难以在程序中寻找到底什么地方进行了强制类型转换
static_cast
编译时尽可能检查
没有继承关系的指针(void* 除外)与引用不能转换
其他类型与指针不能转换
失败:编译失败
dynamic_cast
使用虚表,进行多态类型检查,主要用于父类转子类,运行时检查
失败:指针返回nullptr,引用抛出异常
const_cast
去除对象const或者volatile属性
reinterpret_cast
类似纯C风格的指针类型强转,不常见
异常
构造函数抛异常
对象构造在堆上时,如果构造函数抛出异常,那么对象的堆内存会被自动释放。
但是要注意:如果构造函数分配了其他关联的资源,则需要程序员保证资源可以被全部释放
不允许抛异常的成员函数
- 析构函数
- 移动构造函数
- 移动赋值运算符
- 交换函数(swap)
模板编程
命名修饰
定义:C++编译器后根据命名空间、类名等条件对,函数、变量等实体进行修饰(重命名),实现命名的唯一性
对于C语言兼容:
1 |
|
目的:告诉链接器这是用C语言编译器编出来的,命名没修饰过,找的时候用没修饰的命名找
可变参模板
定义
1 | template <typename T, typename... Args> //typename...标识可变参数 |
参数展开
1 | // 展开函数 |
模板实例化
参数不同,编译器会在生成不同实例的函数
函数对象与函数不同,及时参数相同,也是不同实例的函数
lamda是匿名函数,每一个都会生成不同的实例函数
tuple
定义
- 多元组,固定大小,包含一组异质数据
*
1
using my_tuple = tuple<int, string, string>;
相关工具
- get:获取元素
- tuple_size_v:获取 tuple 的大小
- tuple_cat:拼接多个 tuple
tie
定义
把变量绑定成为引用的 tuple
右值引用与移动
目的
通过实现成员的移动,避免拷贝所造成性能损耗,右值引用的目的是触发移动,C++的函数日常同名,通过参数触发不同函数,一般认为T&&的均为移动类型
左值与右值的定义
左值
- 有标识符、可以取地址
- 非常(non-const)左值可以放在赋值运算符的左侧
- 常见情况
- 变量、函数名字
- 左值对象的成员
- 返回左值引用的表达式,如++x、x = 1、cout << ‘ ‘
- 字符串字面量,如”hello world”
纯右值
- 没有标识符、不能取地址的“临时对象”
- 不可以放在赋值运算符的左侧
- 常见情况
- 返回类型非引用的函数调用或运算符表达式,如x++、1 + 2
- 除字符串字面量外的字面量,如true、42
- lambda表达式
将亡值
- C++11引入,和纯右值合称为“右值”
- 不可以放在赋值运算符的左侧
- 常见情况
- 右值对象或数组的成员
- 返回右值引用的表达式,如std::move(x)
std::move
std::move
:输入左值,返回右值,常用于触发对象移动构造函数和移动赋值函数
1 | std::string str = "Hello"; |
禁止使用std::move操作const对象:退化成了对象拷贝而不是对象移动,带来了性能上的损失
不要使用std::move 返回函数局部变量:阻止了返回值优化
坍缩规则与std::forward
坍缩规则:对引用类型进行引用,从而产生了引用坍缩规则
- (T&)&->T&
- (T&)&& ->T&
- (T&&)& ->T&
- (T&&)&& ->T&&
T&&是转发引用(当其出现在函数模板的参数或变量声明中时)
std::forward\
(x)保持x的引用类型
智能指针
unique_ptr:指向对象地址的unique_ptr只有一个,只能支持移动,不支持拷贝。可以使用make_unique
构造,但不支持类的构造
shared_ptr:引入引用计数,当引用计数为0时,释放对象。可以使用make_shared
构造,优点:对象与引用值只进行一次内存分配,代码简洁
weak_ptr:解决循环指针问题
1 | auto sp9 = std::make_shared<std::vector<int>[]>(4, {5, 6}); |
并发编程
atomic
operator=
:store,memory_order_seq_cst
operator T
:load,memory_order_seq_cst
store
:写操作
load
:读操作
exchange
:交换
内存序:
- memory_order_relaxed:保证原子操作
- memory_order_acquire:禁止CPU与编译器乱序,针对读
- memory_order_release:禁止CPU与编译器乱序,针对写
- memory_order_acq_rel:acquire与release综合
- memory_order_seq_cst:最严格
future
1 | int work(){ |