C++11特性

一、列表初始化
 
1、{}初始化:
#include <iostream>
using namespace std;
struct Point
{
	Point(int x, int y)
		:_x(x)
		,_y(y)
	{
		cout << "Point(int x, int y)" << endl;
	}
private:
	int _x;
	int _y;
};

//一切皆可用{}初始化且可以不加=
//(但是建议日常定义不要去掉=)
int main()
{
	int x = 1;
	int y = { 2 };
	int z{ 3 };

	//本质都是调用构造函数
	Point a1(1, 1);
	Point a2 = { 2,2 };//实质上是多参数隐式类型转换
	Point a3{ 3,3 };

	const Point& r = { 3,3 }; //类型转换产生临时对象具有常性

	int arr1[] = { 0,1,2 };
	int arr2[]{ 0,1,2 };

	Point ptr1[]{ a1,a2 };
	Point ptr2[]{ {0,0},{1,1} };

	int* ptr3 = new int[]{ 1,2,3 };
	Point* ptr3 = new Point[]{ {0,0},{1,1} };
}

2、initializer_list

int main()
{
	//不同的规则
	vector<int> arr = { 0,1,2,3 };//vector支持initializer_list的构造函数
	Point a1{ 1,1 };//直接调用--隐式类型转换

	initializer_list<int> in1 = { 1,2,3 };//常量区数组{ 0,1,2 }存在常量区,本质还是调用initializer_list的构造函数,底层是两个指针(_start和_finish)
	cout << typeid(in1).name() << endl;
	cout << sizeof(in1)<< endl;//两个指针

	int a[] = { 1,2,3 };
	//错误写法:和initializer_list冲突
	//const int* ptr = { 1,2,3 };
}
//支持initializer_list构造函数
		vector(initializer_list<T> It)
		{
			reserve(It.size());
			for (auto e : It)
			{
				push_back(e);
			}
		}

二.、变量类型推导

template<class Func>
class B
{
private:
	Func _f;
};

int main()
{
	//定义变量
	int i = 0;
	auto p = &i;
	auto pf = malloc;

	//auto x无法推导x类型

	//显示类型
	cout << typeid(pf).name() << endl;

	//推导类型
	//typeid(pf).name() ptr//错误,推出类型是一个字符串,只能看不能用
	decltype(pf) pf2;

	B<decltype(pf)> bb;
}
三、新增加容器---静态数组array、forward_list以及unordered系列
 
四、新增接口
1、emplace系列(性能提升) 
2、移动构造和移动赋值
 
五、默认成员函数控制
 
原来C++类中,有6个默认成员函数:
1. 构造函数
2. 析构函数
3. 拷贝构造函数
4. 拷贝赋值重载
5. 取地址重载
6. const 取地址重载
最后重要的是前4个,后两个用处不大。默认成员函数就是我们不写编译器会生成一个默认的。
C++11 新增了两个:移动构造函数和移动赋值运算符重载。
针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:
如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任
意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类
型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,
如果实现了就调用移动构造,没有实现就调用拷贝构造。
如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中
的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内
置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋
值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造
完全类似)
如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。
 
六、左值与右值
1、左值与右值
可以取地址的是左值,左值可以出现在赋值符号的左右。
右值不能取地址,只能出现在赋值符号的右边
int fmin(int a, int b)
{
	return a < b ? a : b;
}

int main()
{
	// 以下为左值
	int* ptr = new int(0);
	int b = 1;
	const int c = 2;
	"xxxxx";//字符串为右值,但此表达式返回首元素地址(为左值)
	const char* p = "xxxxx";
	p[2];

	double x = 1.1, y = 2.2;
	// 以下为右值
	10;
	x + y;
	fmin(x, y);

	return 0;
}

2、左值引用与右值引用

int main()
{
	double x = 1.1, y = 2.2;
	// 左值引用:给左值取别名
	int a = 0;
	int& r1 = a;
	
	// 右值引用:给右值取别名
	int&& r5 = 10;
	double&& r6 = x + y;

	// 左值引用能否给右值取别名?
    // const左值引用可以
	const int& r2 = 10;
    const double& r3 = x + y;//x + y的返回值为临时变量具有常性

	// 右值引用能否给左值取别名?
	// 右值引用可以引用move以后的左值
	int&& r7 = move(a);

	return 0;
}

3、左值引用的使用场景和价值是什么?
使用场景:1、做参数 2、做返回值
价值:减少拷贝

内置类型的右值:纯右值
自定义类型的右值:将亡值

左值:深拷贝
右值:移动拷贝

//string.h

#pragma once

#include<assert.h>

#include<iostream>
using namespace std;

namespace star
{
	class string
	{
	public:	
		string(const char* str = "")
			:_size(strlen(str))
			, _capacity(_size)
			, _str(new char[_capacity+1])
		{
			cout << "string(const char* str)" << endl;
			strcpy(_str, str);
		}
		
		//拷贝构造--深拷贝
		string(const string& s)
			:_str(nullptr)
		{
			cout << "string(const string& s)--深拷贝" << endl;
		}

		//拷贝构造--移动拷贝
		string(string&& s)
			:_str(nullptr)
		{
			cout << "string(string&& s)--移动拷贝" << endl;
			swap(s);
		}

		//析构函数
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		size_t size() const
		{
			return _size;
		}

		void swap(string& s)
		{
			std::swap(_capacity, s._capacity);
			std::swap(_size, s._size);
			std::swap(_str, s._str);
		}
		//左值:深拷贝
		/*string& operator=(string& tmp)
		{
			cout << "string& operator=(string& s)--深拷贝" << endl;
			swap(tmp);
			return *this;
		}*/
		//左值:深拷贝
		string& operator=(const string& s)
		{
			cout << "string& operator=(string& s)--深拷贝" << endl;
			string tmp(s);
			swap(tmp);
			return *this;
		}

		//右值:移动拷贝
		string& operator=(string&& s)
		{
			cout << "string& operator=(string&& s--移动拷贝)" << endl;
			swap(s);
			return *this;
		}

	private:
		size_t _size;
		size_t _capacity;
		char* _str;

	public:
		const static size_t npos;
	};

	const size_t string::npos = -1;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>

using namespace std;

#include"string.h"

star::string Func()
{
	star::string ret = "xxxxxxxxxxx";//"xxxxxxxxxxx"为右值,ret为左值
	return ret;
}

int main()
{
	//ret = 左值--深拷贝
	//ret = 右值--移动拷贝
	star::string ret1 = Func();
	star::string ret2;
	ret2 = Func();

	return 0;
}

ret1编译优化前:
str(左值)先拷贝构造(深拷贝)后移动构造(浅拷贝),Func的返回值是右值。
编译优化后:
连续的构造/拷贝构造,合二为一,编译器把str识别成右值--将亡值。

ret2:编译器也进行了优化处理,把str识别成了右值--将亡值,先移动构造,再移动赋值

//错误写法
star::string& Func()
{
	star::string ret = "xxxxxxxxxxx";//字符串为右值
	return ret;
}

star::string&& Func()
{
	star::string ret = "xxxxxxxxxxx";//字符串为右值
	return ret;
}

错误写法如上,返回值是ret的别名,而ret正常销毁,不会将ret识别成将亡值(只有传值返回才会触发编译器的优化)(引用返回只要不是在栈区上均可)。

七、lambda表达式
 
1、可调用对象:
函数指针--能不用就不用 void(*ptr)(int x)
仿函数--类 重载operator() 对象可以像函数一样使用
lambda--匿名函数对象

2、表达式语法:
[capture-list]捕捉列表
        [var]:表示值传递方式捕捉变量var
        [=]:表示值传递方式捕获所有父作用域中的变量(包括this)
        [&var]:表示引用传递捕捉变量var
        [&]:表示引用传递捕捉所有父作用域中的变量(包括this)
        [this]:表示值传递方式捕捉当前的this指针
(parameters)参数列表(若没有参数传递,可以省略)
→returntype:返回值类型(没有返回值,可以省略)
{statement}:函数体

3、用法示例:
//内部可以调用全局函数,不能调用局部函数。
void func()
{
	cout << "func()" << endl;
}

int main()
{
	int a = 0, b = 2;
	double rate = 2.5;
	auto add1 = [](int x, int y)->int {return x + y; };
	auto add2 = [](int x, int y) {return x + y; };
    //捕捉变量
	auto add3 = [rate](int x, int y) {return (x + y)* rate; };

	cout << add1(a, b) << endl;
	cout << add2(a, b) << endl;
	cout << add3(a, b) << endl;
	//捕捉函数
	auto swap1 = [add1](int& x, int& y) {
		int tmp = x;
		x = y;
		y = tmp;

		cout << add1(x, y) << endl;
		func();
	};
	swap1(a, b);

	return 0;
}
int main()
{
	int x = 0, y = 2;
	//auto swap1 = [x, y]() mutable {
	 mutable让捕捉的x和y可以改变了,
	 但是他们依旧是外面x和y的拷贝
	//	int tmp = x;
	//	x = y;
	//	y = tmp;
	//};
	//swap1();

	// 引用的方式捕捉
	auto swap2 = [&x, &y](){
		int tmp = x;
		x = y;
		y = tmp;
	};
	swap2();

	int a = 0;
	int b = 1;
	int c = 2;
	int d = 3;
	const int e = 1;
	cout << &e << endl;

	// 引用的方式捕捉所有对象,除了a
	// a用传值的方式捕捉
	auto func = [&, a] {
		//a++;
		b++;
		c++;
		d++;
		//e++;
		cout << &e << endl;
	};
	func();

	return 0;
}
int main()
{
	auto f1 = [](int x, int y) {return x + y; };
	auto f2 = [](int x, int y) {return x + y; };

	//f1 = f2;不可以这么赋值,因为类型不同
	cout << typeid(f1).name() << endl;
	cout << typeid(f2).name() << endl;

	return 0;
}

4、底层原理:与仿函数相同

920c8a1b6ef14e62ab0622963758653d.png

 
八、包装器
 
1、可变参数包/可变模版参数
 
//模板可变参数
template <class ...Args>
void ShowList(Args... args)
{
	cout << sizeof...(args) << endl;//打印参数包里值的个数

	// 参数包不支持这么打印
	/*for (size_t i = 0; i < sizeof...(args); i++)
	{
		cout << args[i] << endl;
	}*/
}

//正确打印方式:重载编译递归
void _ShowList()
{
	// 结束条件的函数
	cout << endl;
}

template <class T, class ...Args>
void _ShowList(T val, Args... args)
{
	cout << val << " ";
	_ShowList(args...);
}

模版可变参数使传参更加灵活

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{
		cout << "Date构造" << endl;
	}

	Date(const Date& d)
		:_year(d._year)
		, _month(d._month)
		, _day(d._day)
	{
		cout << "Date拷贝构造" << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};


template <class ...Args>
Date* Create(Args... args)
{
	Date* ret = new Date(args...);

	return ret;
}

int main()
{
	Date* p1 = Create();
	Date* p2 = Create(2023);
	Date* p3 = Create(2023, 9);
	Date* p4 = Create(2023, 9, 27);

	Date d(2023, 1, 1);
	Date* p5 = Create(d);

	return 0;
}

2、emplace系列

1.)emplace_back支持可变参数,拿到构建pair对象的参数后自己去创建对象,除了用法上,和push_back没什么太大的区别
2.)对于拷贝构造和移动构造对象而言,emplace_back是直接构造了,push_back是先构造,再移动构造。

int main()
{
	std::list< std::pair<int, string> > mylist;
	mylist.emplace_back(10, "sort");
	mylist.push_back(make_pair(30, "sort"));

	std::list<Date> lt;
	Date d(2023, 9, 27);
	// 只能传日期类对象
	lt.push_back(d);

	// 传日期类对象
	// 传日期类对象的参数包
	// 参数包,一路往下传,直接去构造或者拷贝构造节点中日期类对象
	lt.emplace_back(d);
	lt.emplace_back(2023, 9, 27);

	return 0;
}

3、包装器--解决可调用对象的类型问题

#include<iostream>
#include<functional>
#include<vector>
using namespace std;

double f(double i)
{
	return i / 2;
}

struct Functor
{
	double operator()(double d)
	{
		return d / 3;
	}
};

int main()
{
	// 包装器 -- 可调用对象的类型问题
	function<double(double)> f1 = f;
	function<double(double)> f2 = [](double d)->double { return d / 4; };
	function<double(double)> f3 = Functor();

	//vector<function<double(double)>> v = { f1, f2, f3 };
	vector<function<double(double)>> v = { f, [](double d)->double { return d / 4; }, Functor() };

	double n = 3.3;
	for (auto f : v)
	{
		cout << f(n++) << endl;
	}

	return 0;
}

4、bind:底层和lambda类似,也是生成仿函数

int main()
{
	function<double(int, int)> Plus1 = bind(Plus, placeholders::_1, placeholders::_2, 4.0);
	function<double(int, int)> Plus2 = bind(Plus, placeholders::_1, placeholders::_2, 4.2);
	function<double(int, int)> Plus3 = bind(Plus, placeholders::_1, placeholders::_2, 4.4);
	cout << Plus1(5, 3) << endl;
	cout << Plus2(5, 3) << endl;
	cout << Plus3(5, 3) << endl;

	function<double(int, int)> PPlus1 = bind(PPlus, placeholders::_1, 4.0, placeholders::_2);
	function<double(int, int)> PPlus2 = bind(PPlus, placeholders::_1, 4.2, placeholders::_2);
	cout << PPlus1(5, 3) << endl;
	cout << PPlus2(5, 3) << endl;

	function<double(int, int)> Sub1 = bind(&SubType::sub, placeholders::_1, placeholders::_2);//静态成员取地址要可加&可不加

	SubType st;
	function<double(int, int)> Sub2 = bind(&SubType::ssub, &st, placeholders::_1, placeholders::_2, 3);//非静态成员取地址要加&,且要再传一个参数(对象地址)
	cout << Sub1(1, 2) << endl;
	cout << Sub2(1, 2) << endl;

	function<double(int, int)> Sub3 = bind(&SubType::ssub, SubType(), placeholders::_1, placeholders::_2, 3);//传对象也可以
	cout << Sub3(1, 2) << endl;

	cout << typeid(Sub3).name() << endl;

	return 0;
}

 

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值