波奇学C++:C++11的新特性

列表初始化

#include<iostream>
using namespace std;
struct A
{
	int _x;
	int _y;
};
int main()
{
	// 三种方式等价,并且可以省略=
	int x = 1;
	int y = { 2 };
	int z{ 3 };
	return 0;
}

{}按声明顺序初始化类成员变量 

A p{ 1,2 };
cout << p._x; //1
cout << p._y; //2

本质上是调用构造函数来初始化,而且在没写构造函数的情况下,还不能用A p(1,2)来初始化。

// 初始化对象
int* ptr = new int[3]{ 1,2,3 };
A* ptr1 = new A[2]{ {1,2},{2,3} };

本质上可以看成多参数隐式类型转换。

struct A
{
	string _x;
	string _y;
};
A p{ "hello","hi" };

C++的initializer_list

auto il = { 10,20,30 };
cout << typeid(il).name();

initalizer_list有两个变量_start指向开头的变量,_finish指向末尾变量

int a[] = { 1,2,3 };
//底层是调用initlize_list的构造函数

vector的C++11支持接收initializer_list列表

用initializer_list来初始化构造函数 

vector<int> v1 = { 1,2,3,4,5 };

decltype():推导类型

decltype(x) y;
//y的类型和x的类型一样

C++11:Null 和 Nullptr

int* p=Null; // 0
int*p1 = nullptr// 0指针

array<> 

array<int,10> a1;
int arr[10];
sizeof(a1);
sizeof(arr);

两个方式差不多,唯一的区别是int arr[]对越界检查更加严格。

forward_list C++中的单链表

forward_list<int> fl;

cbegin()返回const的迭代器

左右值和左右值引用

左值:可以获取地址,一般可以对他赋值,左边可以出现赋值符号左边。

int a=1; // a为左值,能取地址能赋值
const int b=1 // b虽然不能赋值,但能取地址

右值:不能取地址,如函数返回值,字面量等。右值不能在赋值符号右边

double x=1.5,y=1.5;
double z=x+y// x+y为表达式值,是右值
10// 也为右值
"xxxx"//为右值,能取地址是因为表达式返回首元素地址
fun(); //函数返回值也为右值 fun()=1;错误

 纯右值和将亡值

内置类型的右值叫做纯右值 

int add(int x, int y)
{
return x+y;
}

自定义类型的右值叫将亡值, str为将亡值

string func()
{
    string str;
    return str
}

左值引用就是给左值取别名,右值引用就是给右值取别名

int a = 0;
int& r1 = a; //左值
int&& r2 = 5; // 右值

const修饰的左值引用能接收右值

右值引用可以修饰左值加上move() 

const int& r2 = 10;

int a=0;
int&& r3=move(a);

但是虽然两个方法都能接收右值,但时右引用接收的优先级更高

void func(const int& r)
{
	cout << "void func(const int& r)" << endl;
}
void func(int&& r) //传右值优先级更高
{
	cout << "void func(int&& r) " << endl;
}
fun(2);

左值引用的用法一般可以作参数和返回值,作用减少深拷贝,右值能解决传值返回时的优化

String func()
{
    string ret;
    return ret;
}
int main()
{
    string str=func();
    return 0;
}

在上面的代码中str深拷贝生成临时变量,临时变量再赋值给str,至少两次深拷贝。

第一次深拷贝生成临时变量,第二次=赋值重载符临时拷贝变量。

string(const string& s)
			:_str(nullptr)
		{
			cout << "string(const string& s) -- 深拷贝" << endl;
			string tmp(s._str); //创建新的对象
			swap(tmp);
		}

在上面两次的构造中实际上都是利用const string& s 来接收右值,即用左应用来接收右值。

第一次的拷贝构造的左引用指向的是ret值,第二次左引用指向的是临时变量的值。

 为什么拷贝构造要创建新的对象,而移动构造只用交换指针。(不太恰当的比喻)

因为如果拷贝构造相当于接收一个存储值的变量(ret 变量 和 临时变量),如果直接交换指针指向内容,变量空间释放,会使得指针变成野指针。

而移动构造相当于接收一个值。

//拷贝构造
int fun()
{
    int a=1;
    return a;
}
const int& b=a;// 没有创建临时变量(没有创建新对象),a的空间被释放。
int fun()
{
  return 1;
}
int&& b=1; // 可以不创建临时变量。

由此我们理解到右引用相当于跳过接收变量,直接接收值。特别是对于对象之类的。

优化方法我们第二次拷贝赋值时,用右值引用接收它,再交互指针,指针指向空间交换,s临时变量会销毁。

// 移动赋值
		string& operator=(string&& s)
		{
			cout << "string& operator=(string&& s) -- 移动语义" << endl;
			swap(s);
			return *this;
		}

编译器进一步优化

 

 编译器会进一步优化成剩下一步移动构造>

右值引用还能在数据结构对象调用构造函数时减少空间开辟。 

 上图中初始化Node的时候还是会调用拷贝构造,下面中直接把值来过来初始化,调用移动构造。

右值引用的属性默认是左值

// 0是右值,a是左值,可以取地址
int&& a = 0;
cout << &a;

万能引用: 类型&& 万能引用,既可以接收左值,也可以接收右值
实参左值,它就是左值引用(引用折叠),实参右值,就是右值引用。  

template<typename T>
void PerfectForward(T&& t)
{
	Fun(t);
}

右值引用的类型是左值

void Fun(int& t)
{
	cout << "左值引用" << endl;
}
void Fun(int&& t)
{
	cout << "右值引用" << endl;
}
template<typename T>
void PerfectForward(T&& t)
{
	Fun(t);
}
int main()
{
	int&& a = 1;
	PerfectForward(1);
	return 0;
}

输出"左值引用":右值引用会被编译器处理识别成左值。

完美转发:左值数据保存左值属性,右值数据保持右值属性

Forward使得右值引用类型保持右值属性。

void PerfectForward(T&& t)
{
	Fun(forward<T>(t));
}

完美转发的引用:

 void PushBack(T&& x)
 {
 //Insert(_head, x);
 Insert(_head, std::forward<T>(x));
 }

lambda:生成匿名函数,类似于钩子函数。

[capture-list] (parameters) mutable -> return-type { statement }
auto func = [](int x, int y)->int {return x + y; };

func本质上是个对象,和仿函数的作用相似。返回值类型可以省略。

struct greater1
{
	bool operator()(int x, int y)
	{
		return x < y;
	}
};
int main()
{
	// 确定谁在前,x>y 说明当x大的时候,返回true,第一个参数在第二个参数前面,大的参数在前面
	auto func = [](int x, int y) {return x<y; };
	
	vector<int> v = { 1,23,3,31,4,6,7,8,14,56};
	sort(v.begin(), v.end(), func);
	sort(v.begin(), v.end(), greater1());
	sort(v.begin(), v.end(), [](int x, int y) {return x < y; });
	for (auto e : v)
	{
		cout << e << " ";
	}
		return 0;
}

[capture_list]:捕捉列表

传值捕捉:不能修改(除非加上mutable关键字),a,b相当于改变,不影响实参。

auto swap2 = [a, b]()mutable {
	//a, b不能修改 传值捕捉 a的属性是const不能修改的
	// a,b改变不影响外面相当于a,b的拷贝
	int tmp = a;
	a = b;
	b = tmp;
};

传值捕捉父域所有的对象

int main()
{
    int a=0;
    int b=0;
    auto swap2 = [=]()mutable {
	int tmp = a;
	a = b;
	b = tmp;
    };

    return 0;
}

引用捕捉

auto swap3 = [&a, &b]() {
	// 引用捕捉
	double tmp = a;
	a = b;
	b = tmp;
};

引用捕捉:捕捉所有的引用对象

int main()
{
    int a=0;
    int b=0;
    auto swap2 = [&](){
	int tmp = a;
	a = b;
	b = tmp;
    };

    return 0;
}

 引用捕捉所有对象除了a,a是传值捕捉

auto func1 = [&, aa]{};

捕捉lambda函数(实际上底层是个仿函数)

auto add = [](int x, int y)->int {return x + y; };
auto func2 = [add](int& x, int& y)
{
	add(x, y);
};

lambda表达式内可以直接调用全局函数不能调用局部函数。

  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值