C++语言|【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"< \\必须是双斜杠,否则会报错
二、统一的初始化 C++11扩大了用大括号起的列表(初始化列表)使用范围,使其可用于所有的内置类型和用户定义的类型(即类对象)。使用初始化列表时,可添加等号(=),也可不加:
int x={9};
double y={2.25};
vector 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
实现自动类型转换,要求显示初始化,让编译器能够将变量的类型设置为初始值的类型:
autoa = 12; //自动将a转换成int类型;
autoptr = &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 num(new int);
#include void demo1(){ double *pd=new double; //普通开辟了一段内存,但是后续没有释放内存,这样会很危险 *pd=28.9; } //使用智能指针就可以不用考虑这个问题 void demo2(){ auto_ptr ap(new double); //使用智能指针,即使后续不delete,他也会自己释放内存 *ap=26.56; }

由于智能指针模板类的定义方式,智能指针对象的很多方面都类似于常规指针。例如,如果num是一个智能指针对象,则可以对他执行解除引用操作(*num)、用它来访问结构成员(num->m_name)、将他赋给指向相同类型的常规指针。还可以将智能指针对象赋给另一个同类型的智能指针对象。
比如:
auto_ptr ps(new string("abc")); auto_ptr 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 ps(new string("abc")); auto_ptr vo; vo=ps; cout<<*ps<

但是可以使用shared_ptr代替auto_ptr,毕竟shared是共享的意思嘛,所以这是没问题的。
shared_ptr ps(new string("def")); shared_ptr pd; pd=ps; cout<<*ps<

使用unique_ptr代替auto_ptr。
unique_ptr ps(new string("abc")); unique_ptr pd; pd=ps; cout<<*ps<

这样也会报错,但是跟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< cout<
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 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<

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

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

    推荐阅读