【C++碎碎念】C++11新特性(声明、智能指针、右值引用、lambda表达式)

目录

一、新类型

二、统一的初始化

三、声明

四、智能指针

五、右值引用

六、Lambda表达式


一、新类型

C++11新增了long long和unsigned long long,以支持64位(或更宽)的整型,新增了类型char16_t 和 char32_t ,以支持16位和32位的字符表示;还新增了“原始”字符串。

cout<<"C:\\Program Files (x86)\\Dell Digital Delivery Services"<<endl;

\\必须是双斜杠,否则会报错

二、统一的初始化

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

int x={9};

double y={2.25};

vector<int> v{1,2,3,4}; 

class person{

 public:

        string name;

        int age;

        person(string n,int g):name(n),age(g);

};

person s1("xz",18);

三、声明

1、auto

实现自动类型转换,要求显示初始化,让编译器能够将变量的类型设置为初始值的类型:

auto   a = 12; //自动将a转换成int类型;

auto   ptr = &a;  //自动将ptr转换成int *类型,将a的地址赋给ptr;

2、decltype

decltype将变量的类型声明为表达式指定的类型。

decltype(表达式)var;//让var的类型与表达式的类型相同

比如:

int a=10;

decltype(a)b;//  因为a的类型是int,所以b的类型也是int.

double x;

int y;

decltype(x*y) q;//x*y为double型,所以q也为double型

decltype(&x) ab;//&x为double*类型,所以ab也为double*类型

注意:为了确定类型,编译器必须遍历一个核对表

第一步:如果 表达式 是一个没有用括号括起来的标识符,则var的类型与该标识符的类型相同,包括const等限定符;

double &rx=x;

const double * pd;

decltype(rx) u = y;//因为rx是double &类型,所以u也为double &类型

decltype(pd) v; //因为pd为const double *类型,所以v也为const double *类型。

第二步:如果表达式是一个函数调用,则var的类型与函数的返回类型相同

第三步:如果表达式是一个左值,则var为指向其类型的引用。要进入这一步,表达式不能是未用括号括起来的标识符。

double xx=4.4;

decltype((xx)) r2 =xx;//double &;

decltype(xx) w = xx ;//double类型

第四步:如果前面的条件都不满足,则var的类型与表达式的类型相同

int j=3;

int &k=j;

int &n=j;

decltype(j+6) i;//int型

decltype(k+n) x;// int型

decltype((k+n)) //int &型

四、智能指针

如果在程序中使用new从堆(自由存储区)分配内存,等到不需要时,应使用delete将其释放。

C++引入了智能指针auto_ptr,以帮助自动完成这个过程,智能指针是行为类似于指针的类对象。

在使用时(尤其是STL),需要更精致的机制,C++摒弃了auto_ptr,并新增了三种智能指针:

unique_ptr、share_ptr和weak_ptr。

举个例子:

格式:auto_ptr<类型> 变量名(new 类型)

如:auto_ptr<int> num(new int);

#include <memory>
void demo1(){
    double *pd=new double;//普通开辟了一段内存,但是后续没有释放内存,这样会很危险
    *pd=28.9;
} 
//使用智能指针就可以不用考虑这个问题
void demo2(){
    auto_ptr<double> ap(new double);//使用智能指针,即使后续不delete,他也会自己释放内存
    *ap=26.56;
}

由于智能指针模板类的定义方式,智能指针对象的很多方面都类似于常规指针。例如,如果num是一个智能指针对象,则可以对他执行解除引用操作(*num)、用它来访问结构成员(num->m_name)、将他赋给指向相同类型的常规指针。还可以将智能指针对象赋给另一个同类型的智能指针对象。

比如:

auto_ptr<string> ps(new string("abc"));
auto_ptr<string> ab;
ab=ps;//

如果ps和ab都是常规指针,则两个指针指向同一个string对象,这样ps和ab过期时会释放一块内存两次,避免这种问题,有以下几种方式:

1、重载赋值运算符,使之执行深拷贝,这样两个指针指向不同的对象,其中一个对象是另外一个对象的副本;

2、建立所有权概念,对于特定的对象,只能有一个智能指针可拥有它,这样只有拥有对象的智能指针的析构函数会释放该对象。然后,让赋值操作转让所有权,这就是用于auto_ptr和unique_ptr的策略,但unique_ptr的策略更严格;

3、创建智能更高的指针,跟踪引用特定对象的智能指针计数。这就称为引用计数。例如,赋值时,计数将加1,而指针过期时,计数将减1.只有当最后一个指针国企时才会调用delete,这就是shared_ptr采用的策略。

同样的策略也适用于赋值构造函数。

不适用于auto_ptr的实例,因为编译时不会报错,但是执行的时候会报错。

auto_ptr<string> ps(new string("abc"));
auto_ptr<string> vo;
vo=ps;
cout<<*ps<<endl;//当把ps赋给vo时,就相当于把ps的所有权转让给vo了,所以此时解引用读取ps里的值会报错

但是可以使用shared_ptr代替auto_ptr,毕竟shared是共享的意思嘛,所以这是没问题的。

shared_ptr<string> ps(new string("def"));
shared_ptr<string> pd;
pd=ps;
cout<<*ps<<endl;

使用unique_ptr代替auto_ptr。

unique_ptr<string> ps(new string("abc"));
unique_ptr<string> pd;
pd=ps;
cout<<*ps<<endl;

这样也会报错,但是跟auto_ptr的区别就是unique_ptr编译时就会报错.

这么多的智能指针,我到底改选哪一个呢?

如果程序要使用多个指向同一个对象的指针,应该选用shared_ptr;

如果程序不需要多个指向同一个对象的指针,则可以使用unique_ptr;

如果使用new[]分配内存,应该选用unique_ptr;

如果函数使用new分配内存,并返回指向该内存的指针,将其返回类型声明为unique_ptr是不错的选择。

五、右值引用

右值引用就是必须绑定到右值的引用。我们通过&&而不是&来获得右值,右值引用有一个重要的特性-只能绑定到一个将要销毁的对象。

关于左值引用/右值引用的几点说明

1、左值表示一个对象的身份,右值表示对象的值;

int i=42;

int &r=i; //r引用i,这表示左值引用

int &&rr=42;//将rr与42绑定在一起,这是右值引用

int &&aa=i*2;//将aa与表达式i*2绑定在一起。

int &&bb=x+y;//都是临时的数值,用完就释放了

上式相当于把x+y的结果赋给临时变量temp,然后将这个临时变量temp赋给bb这个变量

2、左值引用:&,绑定到左值表达式中,如变量,函数,赋值,下标,解引用,前置递增/递减运算符;

3、右值引用:&&,绑定到字面常量,要求转换到表达式,右值表达式等;

4、左值代表的资源是持久的,右值代表的资源是短暂的;

5、const左值引用:因为是const,无法修改资源,因此绑定到暂时性资源上,也就是绑定到右值引用上;

6、变量是左值,右值引用的本质是变量,所以无法将右值引用绑定到另一个右值引用上。

六、Lambda表达式

lambda表达式又叫匿名函数,简单的理解为没有名称的函数。

lambda表达式很简单那,可以套用如下的语法格式:

[外部变量访问方式说明符](参数)mutable noexcept/throw() -> 返回值类型

{

        函数体;

}

解释:

1、[外部变量方位方式说明符]

[ ] 方括号用于向编译器表明当前是一个lambda表达式,其不能被省略。在方括号内部,可以注明当前lambda函数的函数体中可以使用哪些“外部变量”。

所谓外部变量,指的是和当前lambda表达式位于同一作用域内的所有局部变量。

这个方括号也叫捕获块,主要的类型有以下几种(用的比较多)

[ ] 默认不捕获任何变量;

[=]默认以复制捕获所有变量;

[&]默认以引用捕获所有变量;

[x]仅以复制捕获x,其他变量不捕获;

2、(参数)

和普通函数的定义一样,lambda匿名函数可以接受外部传递的多个参数,和普通函数不同的是如果不需要传递参数则连同()一起省略。

3、mutable

此关键字可以省略,如果要使用的话之前的()就不能省略,默认情况下,对于以值传递引入的外部变量,不允许在lambda表达式内部修改他们的值(比如const常量)。如果想要修改他们,就必须使用mutable关键字。

注意:对于以值传递方式引入的外部变量,lambda表达式修改的是拷贝的那一份,并不会修改外部的值。

int x=10;

auto lam=[x](int a)->int

{

        x++;

        return a+x;

}

cout<<lam(10)<<endl;//21 ,因为x自增1,变为11,11+10=21

cout<<x<<endl; //10,值传递只修改拷贝的那一份,外部不改变值得大小。

4、noexcept/throw()

可以省略,如果使用,在之前的小括号将不能省略,默认情况下lambda函数的函数体可以抛出任何类型的异常,而标注noexcept则不会出现任何的异常。

5、->返回值类型

指明lambda匿名函数的返回值类型,非常智能的是,如果lambda函数体内只有一个return语句,或者该函数返回void,则编译器会自动的识别出返回值类型。

int num[4]={3,2,4,1};

sort(num,num+4,[](int a,int b){return a>b};);//这样会自动识别出返回类型是bool类型。

6、函数体

和普通函数一样,lambda匿名函数包含的内部代码都放置在函数体内,该函数体内除了可以使用指定传递进来的参数外,还可以使用指定的外部变量以及全局范围内的所有全局变量。

具体函数为:

lambda.cpp

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int num[4]={9,8,4,5};
    sort(num,num+4,[=](int a,int b)->bool
    {
        return a>b;//按照从大到小排序
    }
 );
    for(auto a:num)
    cout<<a<<" ";//9 8 5 4
    cout<<endl;
    return 0;
}

使用Linux编译时,使用命令:g++ -std=c++11 lambda.cpp -o lambda,这样编译才不会出错。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值