【C++】总结常见的C++11

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;
}

C++缺省参数、内联函数、auto关键字理解

 

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既可以引用左值,也可以引用右值。

C++引用的理解

 

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对象的上锁和解锁操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值