常用特殊
静态类型动态类型
- 主要区别在于对类型进行检查的时间点。
- 对于所谓的静态类型,类型检查主要发生在编译阶段。
- 对于动态类型,类型检查主要发生在运行阶段(和类型推导相关)。
auto
- auto声明变量的类型必须由编译器在编译时期推导而得。
- auto声明的变量必须被初始化,以使编译器能够从其初始化表达式中推导出其类型。
- 对于指针,auto和auto*没有区别。
- 对于volatile和const,声明为auto的变量并不能从初始化表达式中带走这两种限制符。
1 2 3 4 5 6 7 8 9 10 11 |
double foo(); float* bar(); const auto a = foo(); // a:const double const auto& b= foo(); // b:const double& volatile auto* c = bar(); // c:volatile float* auto d = a; // d:double auto& e = a; // const double& auto f = c; // f:float* volatile auto& g = c; // volatile float* & |
- 不能推导:
- 函数形参不能为auto
- 结构体非静态成员的类型不能是auto
- 不能声明auto数组
- 不能在实例化模板的时候用auto作为参数
typeid
- RTTI机制为每个类型产生一个type_info类型的数据,我们可以在程序中使用typeid查询一个变量的类型,typeid会返回变量相应的type_info数据。
- type_info的name成员返回类型的名字。
- C++11中,增加了hash_code成员函数,返回该类型唯一的哈希值,可以对变量的类型随时进行比较。
decltype
- 推导类型。
- 推导规则:
- 如果e是一个没有带括号的标记符表达式或者类成员访问表达式,那么
decltype(e)
就是e所命名的实体的类型。
如果e是一个被重载的函数,则会导致编译时错误。 - 否则,假设e的类型是T,如果e是一个将亡值,那么
decltype(e)
为T&&。 - 否则,假设e的类型是T,如果e是一个左值,则
decltype(e)
为T&。 - 否则,假设e的类型是T,则
decltype(e)
为T。
- 如果e是一个没有带括号的标记符表达式或者类成员访问表达式,那么
追踪返回类型
1 2 3 4 |
template<typename T1, typename T2> auto Sum(T1& t1, T2& t2)->decltype(t1 + t2) { return t1 + t2; } |
范围for
1 2 |
for (auto e : arr) { } |
类型安全
枚举
缺点:
- 允许隐式转换为整型。
- 占用存储空间及符号性不确定。
强类型枚举
- 强作用域
- 转换限制
- 可以指定底层类型
1 2 3 4 5 6 |
enum class Type : char { General, Light, Medium, Heavy }; |
内存管理问题
- 野指针
- 一些内存单元已被释放,之前指向它的指针却还在被使用。
- 重复释放
- 程序试图去释放已经被释放过的内存单元。
- 内存泄露
- 不再需要使用的内存单元如果没有被释放就会导致内存泄露。
unique_ptr
- 不能与其他unique_ptr类型的指针对象共享所指对象的内存。
- 对内存的这种占用权,可以通过std::move来转移。
- 从实现上,unique_ptr是删除了拷贝构造函数,保留了移动构造函数的指针封装类型。
shared_ptr
- 多个该智能指针可以共享同一堆分配对象的内存。
- shared_ptr使用了引用计数。
weak_ptr
- 指向shared_ptr指针指向的对象内存,却并不拥有该内存。
- 使用lock,可以返回其指向shared_ptr指针指向的对象内存。所在所指对象内存无效时,返回nullptr。
性能
运行时常量
- const,具有运行时数据不可更替性。
编译时常量
- 宏
- constexpr
常量表达式函数
- 函数体只有单一的return返回语句。
- 函数必须返回值。
- 在使用前已经有定义。
- return语句表达式中不能使用非常量表达式的函数、全局数据,且必须是一个常量表达式。
常量表达式值
1 2 |
const int i = 1; constexpr int j = 1; |
- 两者在大多数情况下是没有区别的。
- 如果i是在全局命名空间中,编译器会为i产生数据。
对于j,如果不是有代码显示的使用了它的地址,编译器可以选择不为它生成数据,而仅将其当成编译期的值。 - 对于浮点常量,编译期的浮点常量和实际运行时的浮点常量可能在精度上存在差别。
在C++11中,编译时的浮点数常量表达式是被允许的。 - 对于模板,C++11标准规定,声明为常量表达式的模板函数,如果该模板函数的实例化结构不满足常量表达式的需求的话,constexpr会被自动忽略。
原子操作
- 多线程程序中“最小的且不可并行化的”操作。
- 通常对一个共享资源的操作是原子操作的话,意味着多个线程访问该资源时,有且仅有一个线程在对这个资源进行操作。
- 通常情况下,原子操作都是通过“互斥”的访问来保证的。
原子类型
- C++11原子类型
内存模型
- 内存模型通常是一个硬件上的概念,表示的是机器指令是以什么样的顺序被处理器执行的。
- 现代的处理器并不是逐条执行机器指令的。
- 强序:
- 指令是被按照顺序被处理器执行的
- 弱序:
- 对于某些运行顺序没有影响的指令,一些处理器就可能将其错序执行。
- 对于多线程而言,强序:
- 强序意味着,多个线程看到的执行顺序是一致的。
- 具体地,对于共享内存的处理器而言,需要看到内存中的数据被改变的顺序和机器指令中的一致。
- 对于多线程,弱序:
- 线程间看到的内存数据被改变的顺序于机器指令中声明的不一致。
memory_order
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#include <thread> #include <atomic> #include <iostream> atomic<int> a {0}; atomic<int> b {0}; int ValueSet(int) { int t = 1; a.store(t, memory_order_relaxed); b.store(2, memory_order_relaxed); } int Observer(int) { std::cout << "(" << a << "," << b << ")" << std::endl; } int main() { thread t1(ValueSet, 0); thread t2(Observer, 0); t1.join(); t2.join(); std::cout << "(" << a << "," << b << ")" << std::endl; return 0; } |
线程局部存储
- 所谓线程局部存储变量,就是拥有线程声明期及线程可见性的变量。
- 线程局部存储实际上是由单线程程序中的全局/静态变量被应用到多线程程序中被线程共享而来。
quick_exit
- 该函数并不执行析构函数,而只是使程序终止。
- abort通常是异常退出。
- quick_exit和exit属于正常退出。
at_quick_exit
- at_quick_exit注册的函数也可以在quick_exit的时候被调用。
- 这样一来,可以同样像exit一样做一些清理的工作。
- 在C++11标准中,at_quick_exit和at_exit一样,标准要求至少支持32个注册函数的调用。
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ 51CTO:C++网络通信引擎架构与实现一09/09
- ♥ C++编程规范101规则、准则与最佳实践 二01/07
- ♥ C++11_第五篇12/08
- ♥ 打包_7z生成自解压打包exe07/11
- ♥ COM组件_207/22
- ♥ C++_智能指针08/31