文章目录
1、列表初始化
C++98仅支持内置类型的列表初始化
std::vector<int> vec{1, 2, 3, 4);
C++11支持内置类型和自定义类型的列表初始化
class A
{
public:
A(int _a, int _b)
: a(_a)
, b(_b)
{}
private:
int a;
int b;
}
2、变量类型推导
C++11中,可以使用auto来根据变量初始化表达式类型推导变量的实际类型。
2.1 decltype类型推导
1)推导变量
void Test()
{
int a = 10;
int b = 20;
// 用decltype推演a+b的实际类型,作为定义c的类型
decltype(a+b) c;
std::cout << typeid(c).name() << std::endl;
}
2)推导函数返回值
这里这是推演,不会执行函数。
void* Test(size_t size)
{
return malloc(size);
}
int main()
{
std::cout << typeid(decltype(Test(0))).name() << std::endl;
return 0;
}
2.2 为什么引入decltype?
auto使用的前提是:必须要对auto声明的类型进行初始化,否则编译器无法推导出auto的实际类型。但有时候可能需要根据表达式运行完成之后结果的类型进行推导,因为编译期间,代码不会运行,此时auto也就无能为力。
3、范围for
4、final和override
final:表示该类不能被继承
override:检查派生类有没有对基类的虚函数进行重写
5、智能指针
6、新增容器array、forward_list、unordered系列
7、默认成员函数
C++11中空类中多了以下的默认的成员函数。
7.1 显式缺省函数 default
C++11中,使用 default 关键字,表明该函数使用默认版本,由编译器提供。
class A
{
public:
A() = default;
private:
int _a;
}
7.2 删除默认函数 delete
C++11中,使用 delete 关键字,表明该默认函数不生成
class A
{
public:
A(int a): _a(a)
{}
A(const A&) = delete;
A& operator=(const A&) = delete;
private:
int _a;
}
8、右值引用
右值引用也是别名,但其只能对右值引用。
目的:为了提高程序运行的效率,C++11引入了右值引用
int Add(int a, int b)
{
return a + b;
}
int main()
{
const int&& ra = 10; // 右值也可引用临时变量
int&& ret = Add(10, 30);
return 0;
}
左值和右值的区别
- 一般认为,左值:永久对象,可被取地址,可以出现在 operator= 左侧。
- 右值:临时对象(即将销毁),不可取地址,只能出现在 operator= 右侧。
C++11中右值
- C语言中的纯右值,例如:a+b,100
- 将亡值,比如:表达式的中间结果、函数按照值的方式返回
注意:普通引用只能引用左值,右值引用只能引用右值;const既可以引用左值,也可以引用右值。
8.1 移动语义
概念:将一个对象的资源转移给另一个对象。
移动语义可以解决这个问题:如果一个类中涉及到资源管理,用户必须显式提供拷贝构造、赋值运算符重载以及析构函数,否则编译器将会自动生成一个默认的,如果遇到拷贝对象或者对象之间相互赋值,就会出错。
8.2 完美转发
完美转发是指在函数模板中,完全依照模板的参数的类型,将参数传递给函数模板中调用的另外一个函数。
8.3 右值引用引用左值
当需要用右值引用引用一个左值时,可以通过 move 函数将左值转化为右值。
C++11中,std::move()函数位于 #include <utility>
头文件中。
9、lambda表达式
9.1 lambda表达式语法
[] () mutable -> return-type {}
- [] :捕捉列表,能够捕捉的变量让lambda函数使用
- () :参数列表,没有可以不写
- mutable:lambda是一个const函数,mutable可以取消其常量性,可以省略。
- ->return-type:返回值类型
- {}:函数体
捕捉列表说明:捕捉列表描述了上下文中那些数据可以被lambda使用。
- [var]:表示值传递方式捕捉变量var
- [=]:表示值传递方式捕获所有父作用域中的变量(包括this)
- [&var]:表示引用传递捕捉变量var
- [&]:表示引用传递捕捉所有父作用域中的变量(包括this)
- [this]:表示值传递方式捕捉当前的this指针
捕捉注意:
1)[=, &a, &b]:以引用传递的方式捕捉变量a和b,值传递方式捕捉其他所有变量
2)[&,a, this]:值传递方式捕捉变量a和this,引用方式捕捉其他变量
3)捕捉列表不允许变量重复传递,否则就会导致编译错误。
9.2 lambda表达式应用
struct Goods
{
string _name;
int _price;
}
void Test()
{
Goods gds[] = { { "苹果", 2.1 }, { "梨", 3 }, { "橙子", 2.2 }, {"菠萝", 1.5} };
sort(gds, gds + sizeof(gds) / sizeof(gds[0]), [](const Goods& l, const Goods& r)
->bool
{
return l._price < r._price;
});
}
9.3 函数对象和lambda的区别
函数对象就是仿函数,在类中重载了operator()运算符。
class Rate
{
public:
Rate(double rate): _rate(rate)
{}
double operator()(double money, int year)
{
return money * _rate * year;
}
private:
double _rate;
};
int main()
{
// 函数对象
double rate = 0.49;
Rate r1(rate);
r1(10000, 2);
// lamber
auto r2 = [=](double money, int year)->double{ return monty * rate * year; };
r2(10000, 2);
return 0;
}
10、线程库
在C++11之前,涉及到多线程问题。windows和linux下各有自己的接口,这使得代码的可移植性比较差。要使用标准库的线程,必须包含#include <thread>
头文件。
10.1 线程函数
函数名 | 功能 |
---|---|
thread() | 构造一个线程对象,不关联任何线程函数 |
thread(func1, func2) | 构造一个关联func1和func2线程函数的线程对象 |
get_id() | 获取线程ID |
joinable() | joinable代表的是一个正在执行中的线程 |
join() | 该函数调用后会阻塞主线程,当该线程结束后,主线程继续执行 |
detach() | 用于把被创建线程与线程对象分离开 |
joinable()函数:可以使用jionable()函数判断线程是否是有效的。如果是以下任意情况,则线程无效。
- 采用无参构造函数构造的线程对象
- 线程对象的状态已经转移给其他线程对象
- 线程已经调用jion或者detach结束
join()函数:主线程被阻塞,当新线程终止时,join()会清理相关的线程资源,然后返回,主线程再继续执行,然后销毁线程对象。
detach()函数:该函数被调用后,新线程与线程对象分离,新线程会在后台运行,其所有权和控制权将会交给c++运行库。
thread类不允许拷贝构造以及赋值,但是可以移动构造和移动赋值。
#include <thread>
int main()
{
std::thread t1;
std::cout << t1.get_id() << std::endl;
return 0;
}
10.2 线程关联函数
- 函数对象
- 函数指针
- lambda表达式
#include <iostream>
#include <thread>
class TF
{
public:
void operator()()
{
std::cout << "Thread1" << std::endl;
}
};
void ThreadFunc(int a)
{
std::cout << "Thread2" << a << std::endl;
}
int main()
{
// 线程函数为函数对象
TF tf;
thread t1(tf);
// 线程函数为函数指针
thread t2(ThreadFunc, 10);
// 线程函数为lambda表达式
thread t3([]{std::cout << "Thread2" << std::endl; });
t1.join();
t2.join();
t3.join();
std::cout << "Main thread!" << std::endl;
return 0;
}
11、 原子性操作库
多线程最主要是的就是会有线程安全的问题。因此C++11中引入了原子操作(原子操作:即不可被中断的一个或一系列操作)。C++11引入的原子操作类型,使得线程间数据的同步变得非常高效。
#include <atomic>
int main()
{
atomic_int a(0);
return 0;
}
12、 lock_guard与unique_lock
std::mutex的包含lock(),unlock(),trylock()
lock_guard
template<class _Mutex>
class lock_guard
{
public:
// 在构造lock_gard时,_Mtx还没有被上锁
explicit lock_guard(_Mutex& _Mtx)
: _MyMutex(_Mtx)
{
_MyMutex.lock();
}
~lock_guard() _NOEXCEPT
{
_MyMutex.unlock();
}
lock_guard(const lock_guard&) = delete;
lock_guard& operator=(const lock_guard&) = delete;
private:
_Mutex& _MyMutex;
};
unique_lock类模板也是采用RAII的方式对锁进行了封装,并且也是以独占所有权的方式管理mutex对象的上锁和解锁操作。