划重点,C++11在98的基础上都增加了这些必须学习的重要新特性

零:序

相比较而言,C++11能够更好的用域系统开发和库的开发,语法更加泛化和简单化,更加稳定和安全,不仅功能更加强大,而且能提升程序员的开发效率

一:列表初始化(小重点)

C++11扩大了用大括号括起的初始化列表的适用范围,使其课用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号(=) 也可以不添加。

	//C++11: 支持内置类型的列表初始化
	int a = 1;
	int b = { 1 };
	int c{ 1 };
	float d = { 1.2f };

	//C++11: 支持自定义类型的列表初始化
	vector<int> array3{1, 2, 3, 4, 5};
	vector<int> array2 = { 1, 2 };

	pair<int, int> p = { 1, 1 };
	map<int, int>   m = { { 1, 1 }, { 2, 2 }, { 3, 3 } };
	
	//自定义类型:创建单个对象
	A a3 = { 1, 2};
	A a2(3, 4);
	//单参构造函数的隐式类型转换
	A a4 = 5;

	//多个对象的列表初始化
	//自定义类型:初始化多个元素  ----> 不是天然支持列表初始化
	// 需要显示定义参数类型为initializer_list的构造函数
	
	Vector(const initializer_list<T>& lst)
		:_array(new T[lst.size()])
		, _size(0)
		, _capacity(lst.size())
	{
		for (auto& e : lst)
			_array[_size++] = e;
	}

二:变量类型推导

auto:编译时根据初始化表达式进行类型推导(编译时类型识别)
decltype:运行时类型识别(用结果的实际类型作为函数的返回值类型就不会出错)

	map<string, string> m;
	m["123"] = "456";
	m["789"] = "012";

	std::map<std::string, std::string>::iterator it = m.begin();
	//auto: 编译时根据初始化表达式进行类型推导
	// auto: 编译时类型识别
	auto it2 = m.begin();

	//decltype: 运行时类型识别
	decltype(1 + 2) a; 推演表达式作为变量的定义类型

	decltype(func(10)) b;
	//decltype: 如果有参数列表,推导返回值类型
	cout << "b: type: " << typeid(b).name() << endl;
	//decltype: 如果没有参数列表,只有函数名,推导为函数的接口类型
	cout << typeid(decltype(func)).name() << endl; 推演函数返回值的类型

三:默认成员函数

class C
{
public:
	//default: 让编译器显式生成一个默认函数
	C() = default;
	//C(){}
	//delete: 把一个函数声明成已删除函数,不能再被使用
	// 拷贝构造声明为delete: 防拷贝
	C(const C& c) = delete;
	C& operator=(const C& c) = delete;
private:
	int _c;
};

四:右值引用(重中之重)

1. 左值右值的区别

左值:可以出现在=的两边,或者可以取地址的
其中const类型的常量,因为其可以取地址,则被认为是左值

	int a = 10;
	int b = a;
	int* p = &a;
	int* p2 = &b;

右值:只能够出现在=右边,或不可以取地址的,不是右值的都为左值。
右值又被分为:
纯右值: 常量, 临时变量/匿名变量
将亡值: 声明周期即将结束
临时变量/匿名变量:函数以值返回的变量, 调用类的构造函数创建的变量

2. 引用

引用:左值引用,右值引用,在语法意义上都是变量的别名
在这里插入图片描述

	int a = 10;
	//左值引用:引用的实体既可以为左值,也可以为右值

	//ra : 实体为左值
	int& ra = a;

	// : ri实体为右值
	const int& ri = 10;

	//右值引用:引用的实体只能是右值

	//右值引用: 实体为常量
	int&& lr = 10;
	//右值引用: 实体为临时变量
	int&& lr2 = getA(a);

	const int& r3 = getA(a);

	//右值引用: 不能引用左值
	//int&& r4 = a;

3. 移动语义

在这里插入图片描述
移动构造:

//移动构造: 提高拷贝效率
	String(String&& str)
		:_str(str._str)
	{
		str._str = nullptr;
		_size = _capacity = str._size;
		cout << "String(String&&)" << endl;
	}

移动赋值:

//移动赋值
	String& operator=(String&& str)
	{
		if (this != &str)
		{
			swap(_str, str._str);
			_size = _capacity = str._size;
			cout << "String operator=(String&&)" << endl;
		}

		return *this;
	}

浅拷贝: 移动构造, 直接获取将亡值(右值)的资源String ret = getString();
深拷贝: 拷贝构造, ret为左值String copy(ret);
String ret2 = String("456"); 构造 + 拷贝构造 , 优化: 构造

move: 当需要右值引用引用一个左值时,可以通过move函数将左值转换为右值!(被转换的左值,其生命周期并没有随着左值的转换而改变,即转换的佐治变量不会被销毁)

int a = 10;
	//std::move:  移动语义--> 把变量的属性变成右值 
	int&& rr = move(a);
	String str("123");
	//移动语义错误示例:把一个后面会用到的左值变成了一个右值
	String copy(move(str));
	
	//移动语义正确场景:需要保证属性被修改的左值后面不会再用到
	Person(Person&& person)
		:_name(move(person._name))
	{
		cout << "Person(Person&&)" << endl;
	}

4. 完美转发

是指在函数模板中,完全依照模板的参数类型,将参数传递给函数模板中调用的另外一个函数

void Fun(int& x) { cout << "lvalue ref" << endl; }
void Fun(int&& x) { cout << "rvalue ref" << endl; }
void Fun(const int& x) { cout << "const lvalue ref" << endl; }
void Fun(const int&& x) { cout << "const rvalue ref" << endl; }

//特殊: 模板参数: T&& 未定引用类型---> 主要看模板参数接收的实际类型,和实际类型匹配
template<typename T>
//void PerfectForward(const T& t){ Fun(t); }
void PerfectForward(T&& t) { Fun(std::forward<T>(t)); }
void testForward()
{
	PerfectForward(10); // rvalue ref
	int a = 0;
	PerfectForward(a); // lvalue ref
	PerfectForward(std::move(a)); // rvalue ref
	const int b = 8;
	PerfectForward(b); // const lvalue ref
	PerfectForward(std::move(b)); // const rvalue ref

}

五:lambda表达式

C++98中的元素排序:

	int array[] = { 4, 1, 8, 5, 3, 7, 0, 9, 2, 6 };
	// 默认按照小于比较,排出来结果是升序
	std::sort(array, array + sizeof(array) / sizeof(array[0]));
	// 如果需要降序,需要改变元素的比较规则
	std::sort(array, array + sizeof(array) / sizeof(array[0]), greater<int>());

如果待排序元素为自定义类型,则需要自定义排序比较规则

struct LessA
{
	bool operator()(const A& a1, const A& a2)
	{
		return a1 < a2;
	}
};

struct GreaterA
{
	bool operator()(const A& a1, const A& a2)
	{
		return a1 > a2;
	}
};

1. C++11中的lambda表达式

std::sort(array, array + sizeof(array) / sizeof(array[0]), [](const A& a1, const A& a2)->bool
		{
			return a1 < a2;
		});

lambda表达式书写格式:[capture-list] (parameters) mutable -> return-type { statement }

  • [capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。
  • (parameters):参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略
  • mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)
  • ->returntype:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导
  • {statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量
	int a = 10;
	int b = 5;
	//最简单的lambda表达式
	[] {};
	//mutable: 把捕捉列表中的变量属性改为非const(默认是const属性)
	[a, b]()mutable {a = 100; b = 200; return a + b; };
	auto func = [](int a, int b)->int {a = 1; b = 2; return a + b; };
	//lambda表达式使用
	func(a, b);

在这里插入图片描述

	int a = 1;
	int b = 2;
	cout << a << " " << b << endl;
	//[=]:以传值形式捕捉父类作用域的所有变量
	auto func1 = [=](int num)mutable->int {
		a = 5;
		b = 10;
		//c不能捕捉, c还没有定义
		//return a + b +c + num;
		return a + b + num;
	};

	func1(300);
	cout << a << " " << b << endl;
	//如果是传引用的形式,不需要mutable也可以修改捕捉列表中的变量
	auto func2 = [&](int num)->int {
		a = 5;
		b = 10;
		//c不能捕捉, c还没有定义
		//return a + b +c + num;
		return a + b + num;
	};

	func2(300);
	cout << a << " " << b << endl;

	int c = 3;

	//[=,&a]除过a之外, 其它变量以值的形式捕捉,a以引用的形式捕捉
	//[=,a]: 错误写法
	auto func3 = [=, &a](int num)->int {
		return a + b + num;
	};
	//[&,a]: 除过a之外, 其它变量以引用的形式捕捉,a以值的形式捕捉
	auto func4 = [&, a](int num)->int {
		return a + b + num;
	};
  • 父类作用域不一定就是直接父类作用域,也可以是嵌套父域
class C
{
public:
	void printC()
	{
		int a = 1;
		int b = 2;
		//父类作用域不一定就是直接父类作用域,也可以是嵌套父域
		auto func1 = [=](int num)->void {
			//a,b属于C::printC函数的局部域
			cout << a << " " << b << " " << this->_c << endl;
			//_c属于类域C
			cout << _c << endl;
		};
		func1(30);
	}
public:
	int _c = 5;
};
  • 如果不是局部域,捕捉列表中不能指定具体的变量,但是可以写=或&
auto funcG = [=](int a, int b)->void {

	cout << global << endl;
};
  • lambda表达式之间不能赋值,但是可以拷贝。
	auto fun3(fun2);
	auto fun4 = fun2;
	fptr ptr;
	//可以把lambda表达式赋给一个函数指针
	ptr = fun1;
  • C++实现lambda表达式: 创建一个仿函数类

六:线程库(重要)

1. 线程

C++11中最重要的特性就是对线程的支持,使得C++在并行编程时不需要依赖第三方库,且引入了原子操作的原子类概念。
头文件:<thread>

	thread t1(tfunc1);
	thread t2(tfunc2, 1);
	thread t3(tfunc3, 1, 2, 3);
	cout << "线程等待" << endl;
	t1.join();
	t2.join();
	t3.join();
	return 0;

采用RAII的方式对线程进行封装,是防止join无法进行销毁的

//RAII: 资源获取立即初始化
//  在构造函数中初始化资源
//  在析构函数中销毁资源
class ThreadManage
{
public:
	ThreadManage(thread& t)
		:_thread(t)
	{}

	~ThreadManage()
	{
		if (_thread.joinable())
			_thread.join();
	}
private:
	thread& _thread;
};

void testThread2()
{
	thread t1(tfunc1);
	thread t2(tfunc2, 1);
	thread t3(tfunc3, 1, 2, 3);
	ThreadManage tm1(t1);
	ThreadManage tm2(t2);
	ThreadManage tm3(t3);

	return;
}

如果函数为成员函数

void testClassFunc()
{
	ThreadClass tc;
	//如果函数为成员函数,则需要写完整作用域,并且需要显示取地址,参数需要加上this指向的对象
	thread t1(&ThreadClass::funcT, &tc, 10);
	t1.join();
}
void testThreadRef()
{
	int a = 0;
	cout << a << endl;
	//如果函数参数类型为引用,在线程中需要修改原始的变量,则需要通过ref转换
	thread t1(func2, ref(a));
	t1.join();
	cout << a << endl;
	return 0;
}

join和detach的区别:
join:主线程被阻塞,当新线程终止时,join()会清理相关的线程资源,然后返回,主线程再继续向下执行,然后销毁线程对象。由于join()清理了线程的相关资源,thread对象与已销毁的线程就没有关系了,因此一个线程对象只能使用一次join(),否则程序会崩溃。
detach():该函数被调用后,新线程与线程对象分离,不再被线程对象所表达,就不能通过线程对象控制线程了,新线程会在后台运行,其所有权和控制权将会交给c++运行库。同时,C++运行库保证,当线程退出时,其相关资源的能够正确的回收

2. 原子操作

#include <atomic>
	sum(0);
	atomic<int> sum(0);

	number n;
	atomic<number> atomic_number(n);

加锁:

mutex mtx;
int global = 0;
void fun3(int num)
{
	for (int i = 0; i < num; ++i)
	{
		//mtx.try_lock: 非阻塞加锁操作:如果其它线程没有释放当前的锁,则直接返回加锁失败的结果
		//mtx.lock:  阻塞加锁操作:如果其它线程没有释放当前的锁,阻塞等待,直到其它线程释放当前锁
		mtx.lock();
		//如果当前线程拥有该锁,执行第二次的加锁操作,会导致死锁
		//mtx.lock();
		++global;
		mtx.unlock();
	}
		
}

守卫锁:

//守卫锁
template <class Mutex>
class lockGuard
{
public:
	//构造函数加锁
	lockGuard(Mutex& mtx)
		:_mtx(mtx)
	{
		//cout << "lockGuard(Mutex&)" << endl;
		_mtx.lock();
	}

	//析构函数解锁
	~lockGuard()
	{
		//cout << "~lockGuard" << endl;
		_mtx.unlock();
	}

	//防拷贝
	lockGuard(const lockGuard<Mutex>&) = delete;
	lockGuard& operator=(const lockGuard<Mutex>&) = delete;
private:
	Mutex& _mtx;
};

void fun4(int num)
{
	for (int i = 0; i < num; ++i)
	{
		//创建守卫锁
		lockGuard<mutex> lg(mtx);
		//防拷贝
		//lockGuard<mutex> copy(lg);
		++global;
	}

}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Luckily0818/article/details/107753161