C++11特性

目录

1.std::initializer_list

2.auto 自动推导类型

3.范围for

4.智能指针

5.右值引用和移动语义

6.lambda表达式

前言:

C++11针对C++98/03带来了许多新的特性,在很多使用方面得到了很大的改善,本文主要阐述C++11比较重要的特性。

1.std::initializer_list

很多同学看到这个词感到十分陌生,我如果放上使用例子的话,大家会十分的熟悉。

使用例子:

vector<int> v1 = {1,2,3,4,5};
map<int,string> = {{1,"apple"},{2,"tree"}}

通常用于STL容器的初始化,这样初始化十分的方便,快捷!

2.auto 自动推导类型

auto在C++11中,也是经常使用到的。auto是用于推导变量的类型。常与范围for连用。

优势在于使用起来十分的方便!因为有的变量类型是十分繁琐的,我们可以通过auto进行简写。

例如:

vector<vector<map<int,string>>> v1;

vector<vector<map<int,string>>> v2 =v1;

auto v2 = v1;

通过auto看上去直接清晰明了。

3.范围for

它的工作原理是通过迭代器,进行遍历整个STL容器。

通过auto自动推导类型,再进行自叠加。

4.智能指针

智能指针是一个很重要的概念,因为C++中,自己开辟的空间,需要自己去释放掉,如果忘掉释放空间,会造成内存泄漏等危害。而智能指针便是为了解决这样的事情而产生的。

auto_ptr:

auto_ptr的实现原理其实就是RAII,在构造的时候获取资源,在析构的时候释放资源,并进行相关指针操作的重载,使用起来就像普通的指针。但是在C++11中已经被弃用!

unique_ptr:

unique顾名思义:唯一!是的unique_ptr具有唯一性,说白了,就是unique_ptr不可以拷贝构造!并且一个常规的指针,只能有一个unique_ptr。原因在于:unique_ptr本身是一个对象,在析构的时候,就会将原来的指针进行释放,如果有多个unique_ptr指向同一个指针,那么就会造成多次释放,就会报错。

int* a = new int(1);
unique_ptr<int> b(a);
unique_ptr<int> c(a);

 但可以进行移动构造(move),在移动构造以后,指针的权限全部转移到新的unique_ptr,并且原来的unique_ptr会被置空

例子:

int* a = new int(1);
unique_ptr<int> b(a);
unique_ptr<int> d = move(b);  移动构造

shared_ptr

shared_ptr是较为完善的智能指针,它具有引用计数的功能,只有在计数为0的时候,才会释放掉原来的指针!但是shared_ptr并没有解决循环计数的问题!

int* a = new int(1);
shared_ptr<int> p1(a);
shared_ptr<int> p2 = p1;
shared_ptr<int> p3(p1);
shared_ptr<int> p4 = a; 错误用例!

weak_ptr

weak_ptr更像是shared_ptr的小跟班。它只能接受shard_ptr,并且不会产生引用计数

int* a = new int(1);
shared_ptr<int> p3(a);
weak_ptr<int> p5 = p3;
weak_ptr<int> p4(p3);  两种方式接收 shared_ptr

5.右值引用和移动语义

1.左值引用和右值引用

没有谈到右值引用之前,我们学过引用——左值引用。无论是左值引用还是右值引用都是取别名!

左值是一个表示数据的表达式(如变量名解引用的指针),我们可以获取它的地址+可以对它赋
值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。

int main()
{
// 以下的p、b、c、*p都是左值
int* p = new int(0);
int b = 1;
const int c = 2;
// 以下几个是对上面左值的左值引用
int*& rp = p;
int& rb = b;
const int& rc = c;
int& pvalue = *p;
return 0;
}

什么是右值和右值引用呢?

右值也是一个表示数据的表达式,如:字面常量表达式返回值函数返回值(这个不能是左值引
用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能
取地址
。右值引用就是对右值的引用,给右值取别名。

int main()
{
double x = 1.1, y = 2.2;
// 以下几个都是常见的右值
10;
x + y;
fmin(x, y);
// 以下几个都是对右值的右值引用
int&& rr1 = 10;
double&& rr2 = x + y;
double&& rr3 = fmin(x, y);
// 这里编译会报错:error C2106: “=”: 左操作数必须为左值
10 = 1;
x + y = 1;
fmin(x, y) = 1;
return 0;
}

2.左值引用和右值引用比较

左值引用总结:
        1. 左值引用只能引用左值,不能引用右值。
        2. 但是const左值引用既可引用左值,也可引用右值

int main()
{
// 左值引用只能引用左值,不能引用右值。
int a = 10;
int& ra1 = a; // ra为a的别名
//int& ra2 = 10; // 编译失败,因为10是右值
// const左值引用既可引用左值,也可引用右值。
const int& ra3 = 10;
const int& ra4 = a;
return 0;
}

右值引用总结:
        1. 右值引用只能右值,不能引用左值。
        2. 但是右值引用可以move以后的左值。

int main()
{
// 右值引用只能右值,不能引用左值。
int&& r1 = 10;
// error C2440: “初始化”: 无法从“int”转换为“int &&”
// message : 无法将左值绑定到右值引用
int a = 10;
int&& r2 = a;
// 右值引用可以引用move以后的左值
int&& r3 = std::move(a);
return 0;
}

已知:const 左值引用 可以接收左值和右值,那么右值引用的用处在哪里呢?

答案是:如果是一个临时变量,作为返回值时,不可以用左值引用,因为临时变量在函数结束时,会自动销毁!那么这时右值引用可以派上用场!

如果返回值再复杂一点,是一个类的话,使用右值引用会更加的方便。下面让我实操给大家看看!

 普通的传值返回,至少会造成一次拷贝构造!(如果编译器不优化,会有两次拷贝构造!)

如果是采用右值引用的话,就会优化这个问题!

移动构造
A(A&& a)
{
	cout << "A的移动构造" << endl;
	swap(a);
}


A to_A(int a)
{
    A ret(a);
    return ret;
}

这里的返回值是一个右值。如果既有拷贝构造,又有移动构造的话,
编译器会选择移动构造,因为移动构造可以减少深拷贝

运行结果:

6.lambda表达式

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

各部分说明:
[capture-list] : 捕捉列表.捕捉列表能够捕捉上下文中的变量供lambda函数使用。
(parameters):参数列表.与普通函数的参数列表一致,如果不需要参数传递,则可以
连同()一起省略

mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量
性。使用该修饰符时,参数列表不可省略(即使参数为空)。

->returntype:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回
值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推
导。
{statement}:函数体。

捕捉列表说明:

捕捉列表描述了上下文中那些数据可以被lambda使用,以及使用的方式传值还是传引用。
[var]:表示值传递方式捕捉变量var
[=]:表示值传递方式捕获所有父作用域中的变量(包括this)
[&var]:表示引用传递捕捉变量var
[&]:表示引用传递捕捉所有父作用域中的变量(包括this)
[this]:表示值传递方式捕捉当前的this指针        

实例:

int a = 3, b = 4;
[=]{return a + 3; };
捕捉上下文中的所有变量
()进行省略
返回值自行推导

省略了返回值类型,无返回值类型
auto fun1 = [&](int c){b = a + c; };
fun1(10)
cout<<a<<" "<<b<<endl;

// 各部分都很完善的lambda函数
auto fun2 = [=, &b](int c)->int{return b += a+ c; };
cout<<fun2(10)<<endl;
b以引用的方式进行传递,其余变量值传递。

// 复制捕捉x
int x = 10;
auto add_x = [x](int a) mutable { x *= 2; return a + x; };
cout << add_x(10) << endl;

lambda表达式实际上可以理解为无名函数,该函数无法直接调
用,如果想要直接调用,可借助auto将其赋值给一个变量

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值