本文综合了候捷老师「C++11/C++14课程」的内容及一些其他的自己已知的C++11的内容。
vector::shrink_to_fit()
减少容量,适应实际大小。(根据当前容量大小调用分配器的realloc
版本)
比如当前当前容量为256,存放了129个元素(扩容部分仅用了一个),调用该接口可以调整空间容量为
129
std::function
std::function的实例可以对任何可以调用的目标实体进行存储、复制和调用操作。这些目标实体包括普通函数、Lambda表达式、函数指针以及其它函数对象等。(升级版函数指针)
1 | //普通函数 |
智能指针
new分配内存,智能指针可以自动释放。而传统指针需要手动delete。
智能指针对普通的指针进行封装,负责自动释放所指的对象,这样的一层封装机制的目的是为了使得智能指针可以方便的管理一个对象的生命期。
- auto_ptr(c++11弃用)
1 | auto_ptr<string> p1(new string("I reigned loney as a cloud.")); |
此时p2剥夺p1所有权。访问p1会报错。(存在内存崩溃的风险)
unique_ptr
实现独占式拥有或严格拥有概念,保证同一时间内只有一个智能指针可以指向该对象。避免资源泄露
1 | unique_ptr<string> p3(new string("I reigned loney as a cloud.")); |
shared_ptr
实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时候释放。用计数机制来表明资源被几个指针共享。
多个线程同时读同一个shared_ptr对象是线程安全的,但是如果是多个线程对同一个shared_ptr对象进行读和写,则需要加锁。
多线程读写shared_ptr所指向的同一个对象,不管是相同的shared_ptr对象,还是不同的shared_ptr对象,也需要加锁保护。shared_ptr拥有成员函数如下:
- use_count — 引用计数的个数
- unique — 是否独占
- swap — 交换两个shared_ptr所拥有的对象
- get — 返回内部对象
weak_ptr
- 尝尝与shared_ptr搭配使用,为shared_ptr的观察者。
- 不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象。
- 当 shared_ptr 管理的资源被释放时,weak_ptr 会自动变成 nullptr。
- 只是提供了对管理对象的一个访问手段。用来解决shared_ptr相互引用时的死锁问题
类型转换
const_cast
— 将const type
转化为type
(type为指针或引用)static_cast
— 可以用于各种编译器认可的隐式转换(类对象向上转换)dynamic_cast
— 用于含有虚函数的类的转换,用于类向上和向下转换reinterpret_cast
— 任意类型转换,不保证成功。
1 | //static_cast |
Variadic Template
数量不定的模板参数。
...
用来表示一包pack
。表示模板参数、函数参数、函数参数类型的数量不确定。(用来递归)
1 | void print() {}//递归出口 |
作用域
- 函数模板
- 类模板
变化的是模板参数
- 参数个数(参数个数逐一递减,实现递归)
- 参数类型(参数类型随参数个数也变化)
重写printf
1 | //无参数版本 |
递归继承 — tuple
Spaces in Template Expressions
模板表达式中的空格。
在过去的版本中,vector<vector<int> > res
,右侧的> >
中间必须有空格,不然编译器会以为是个操作符。该版本之后中间的空格可以取消。
nullptr
C++11支持使用 nullptr
代替0 NULL
,其类型为std::nullptr_t
1 | void func(int); |
Automatic Type Deduction
auto
自动类型推断。在模板的应用中,我们已经知道编译器可以推导实参的类型,在此将此能力表现。
名称特别长懒得打字、类型复杂一时想不出时推荐使用。
1 | auto i = 42; |
Uniform Initialization
一致性初始化。
在C++11之前,初始化可能会发生在()、{}、=
操作中。程序员很容易困惑于:初始化变量、对象时怎么写?。基于此原因,导入uniform initialization
。保证任何初始化都可以使用一种语法{}
1 | //一致性初始化 |
initializer_list
编译器看到{..}
便生成initializer_list<T>
,其内部关联到容器array<T,n>
。调用构造函数时(初始化时调用的是构造函数ctor
),该array
内的元素可被编译器分解,逐一传给函数。
函数参数如果是
initializer_list<T>
,调用者不能传入多个initializer_list<T>
。可以利用
{}
来赋空值
1 | int i{}; // i == 0 |
- 不推荐向低精度转化
int x{5.0}
自定义函数时,如果想通过简单的方式来接收任意个数参数,可以使用initializer_list
。
比起
...
参数的类型比较严格。
1 | void print(std::initializer_list<int> vals) { |
样例
1 | class P |
上述样例中如果没有
func2
,则对象b d
仍会创建,调用func1
源码
1 | template<class _E> |
多个同类型的参数max/min通过initializer_list实现
Explicit
作用于含有一个以上实参的构造函数,防止编译器进行隐式类型转换。
C++中nonexplicit one argument ctor
才能做隐式转换。
range-base for
1 | for-loop`本来的格式有三段,现可以简化为两段`for(decl : coll) |
=default/=delete
如果有定义构造函数(ctor),则编译器不会再提供默认构造函数(default ctor),如果强制加上=default
,可以重新获得并使用default ctor
。主要用于构造函数、拷贝构造、拷贝赋值、析构函数(不写的话,编译器会自动增加的函数)
1 | class Zoo{ |
=delete
禁用成员函数的使用。删除特殊成员函数提供了一种更简洁的方法来防止编译器生成我们不想要的特殊成员函数。
Alias Template
模板别名。在专门化别名模板时生成的类型不允许直接或间接地使用其自己的类型:
1 | template<T> |
#define
typedef
无法达到相同效果。#define
会改变模板中的参数,从而形成类似于偏特化的东西typedef
不接收参数,也无法达到预期效果。
template template parameter
模板模板参数则是模板的参数又是一个模板,如下
1 | template<typename T, template<typename U> typename Container> |
注意事项
以下不为模板模板参数
1 | template<typename T, typename Sequence = list<T>> |
这里是容器指定底层容器,虽然使用了模板参数,但两个参数一旦指定前一个,后一个随之确定。
而模板模板参数两个参数之间没有任何关系。
Type Alias
类型别名,类似于typedef
。
1 | typedef void (*func)(int,int); //参数为int,int返回值为void的函数指针 |
using
- using-directives 命名空间(
using namespace std
)、using-declarations 某个函数(using std::function
) - using-declarations成员函数(using _Class::xxx)功能同上
- type alias 、alias template去替换
noexcept
不抛出异常。C++中的异常处理是在运行时而不是编译时检测的。为了实现运行时检测,编译器创建额外的代码,然而这会妨碍程序优化。
1 | void func() noexcept(true);//满足条件时不抛出异常 |
override
重写。子类在想要重写父类的虚函数时,声明为override
(防止子类写错,不是重写虚函数则报错)可以显式的在派生类中声明,哪些成员函数需要被重写,如果没被重写,则编译器会报错。如果不小心漏写了虚函数重写的某个苛刻条件,也可通过编译器的报错,快速定位错误
1 | class Base { |
final
用于修饰类、成员变量和成员函数。
- final修饰的类,不能被继承,其中所有的函数都不能被重写。
- final修饰的成员函数不能被重写。
- final修饰的变量不能更改。
decltype
类型自动推导
- 用来声明返回值类型。
1 | template<typename T1,typename T2> |
- 函数模板设计中获取某对象类型
1 | template<typename T> |
- 获取
lambda
表达式的返回值的类型(用来声明)
1 | auto cmp = [](const Person1& p1,const Person2& p2){...}; |
Lambda
无名仿函数。允许定义内联函数,用来当成参数、对象使用。是一组功能的定义,可以被定义在表达式里。
声明
1 | [CaptureList] (ParamsList) mutable exception-> ReturnType { FunctionBody } |
CaptureList
— 捕获外部变量列表ParamsList
— 形参列表mutable
— 用来说明是否可以修改捕获的变量exception
— 异常设定ReturnType
— 返回类型FunctionBody
— 函数体
省略形式
序号 | 格式 |
---|---|
1 | [CaptureList ] (ParamsList ) -> ReturnType {FunctionBody } |
2 | [CaptureList ] (ParamsList ) {FunctionBody } |
3 | [CaptureList ] {FunctionBody } |
- 格式1声明了
const
类型的表达式,这种类型的表达式不能修改捕获列表中的值。 - 格式2省略了返回值类型,但编译器可以根据以下规则推断出Lambda表达式的返回类型
- 如果
FunctionBody
中存在return
语句,则该Lambda表达式的返回类型由return
语句的返回类型确定。 - 如果
FunctionBody
中没有return
语句,则返回值为void
类型。
- 如果
- 格式3中省略了参数列表,类似普通函数中的无参函数。
参数详解
CaptureList
Lambda表达式与普通函数最大的区别是,除了可以使用参数以外,Lambda函数还可以通过捕获列表访问一些上下文中的数据。
- []、[var]、[&var]表示不捕获、值传递、引用传递
- [=]表示值传递方式捕获所有父作用域的变量
- [&]表示引用传递方式捕捉所有父作用域的变量
ParamsList
除了捕获列表之外,lambda还可以接受输入参数。参数列表是可选的。
mutable
mutable修饰符, 默认情况下Lambda函数总是一个const
函数,mutable
可以取消其常量性。
在使用该修饰符时,参数列表不可省略。
exception
指示 Lambda 表达式不会引发任何异常。
ReturnType
返回类型会自动推导
FunctionBody
可以包含普通方法或函数的主体可以包含的任何内容。
样例
1 | vector<int> myvec{ 3, 2, 5, 7, 3, 2 }; |
Rvalue reference
右值引用。
通俗来说,可以取地址、有名字的为左值。(在内存中有实际地址的值、可以出现在赋值左侧的值)
右值引用就是对一个右值进行引用的类型。左值引用就是对一个左值进行引用的类型。
为了解决非必要的拷贝。当赋值的右侧为一个右值时,左侧可以在右侧偷出值,而不需要调用构造器。
左值引用
左值引用包括常量左值引用和非常量左值引用。非常量左值引用只能接受左值,不能接受右值;
1 | int &a = 2; // 非常量左值引用 绑定到 右值,编译失败 |
右值引用
右值引用独立于左值和右值。即,右值引用类型的变量可能是左值也可能是右值。
1 | int&& var1 = x; |
var1类型为右值引用,但var1本身是左值,因为具名变量都是左值。
T&& 并不一定表示右值,它绑定的类型是未定的,既可能是左值又可能是右值。
1 | template<typename T> |
std::move()
该函数并不能移动任何东西,它唯一的功能是将一个左值强制转化为右值引用,继而可以通过右值引用使用该值,以用于移动语义。从实现上讲,std::move基本等同于一个类型转换static_cast<T&&>(Lvalue);
Perfect forwarding
nonperfect forwarding
— 当我们对一个右值调用函数时,右值会调用move
函数,然后调用函数时调用相应的左指的函数。此时即为一个不完美的交付
1 | void process(int& i){ cout<<"process(&)"<<i<<endl;} |
标准库中提供函数std::foward
来实现完美的交付
容器array
没有ctor
,没有dtor
。封装成类的数组。创建时指定大小,不可扩容。
Tuple
元组是将不同类型的元素打包到一个对象中使用,就像pair
适用于成对的元素,但tuple
可以泛化为任意数量的元素。相关函数如下
tuple_size()
— 获取元组中元素的个数tuple_element()
— 访问tuple中指定位置的元素make_tuple()
— 构造包含指定内容的元组get<index>()
— 获取元组中第index个元素
1 | tuple<int,double,string> temp(11,42.2,"xxx");//手动建元组 |