【C++】C++11新特性

C++ 11新特性

1.新增了原始的字符串等新类型

   若输出需要进行多次转义例如输出文件路径时可以使用原始类型输出,不用加转义字符\

 cout<<C:\\Program Files (x86)\\Application Verifier<<endl;
 cout<<R"(C:\\Program Files (x86)\\Application Verifier)"<<endl;
 cout<<R"+*(C:\\Program Files (x86)"\\Application Verifier)+*"<<endl;//可以避免内部有"的情况导致匹配错误。

2.统一的初始化以及成员变量默认的初始化

   扩大了大括号初始化列表的使用方位,可以适用于所有的内置类型及用户定义类型,且可以不添加等号:

 int x={5};
 int *arr=new int[5]{1,2,5,6,7};
 vector<int> v1{2,3,4,5,6};

   构建对象时成员变量可以不用构造函数,可以在类中直接默认进行初始化

 class Person{
     public:
         string m_Name="TOM";
         int m_Age=10;
 }

3.声明的简化

   auto关键字:实现自动类型推导,根据变量的初始值来推导出变量类型所以要求进行显示初始化,不能先声明后赋值

 auto a=12;//默认推导int类型
 int (*p)(int,int)=add;//函数指针
 auto ptr=add;//推导函数指针类型
 vector<int> v;
 auto it=v.begin();//推导迭代器vector<int>::iterator it=v.begin();

   decltype关键字:和auto的功能一样,都用来在编译时期进行自动类型推导,但是decltype根据expression表达式推导出变量的类型,跟"="右边的value没有关系,使用时可以不进行赋值操作

 decltype(expression) var;
 decltype(x) y;//让y的类型同x,x可为值、表达式、函数
 /*
 1.如果expression为没有括号括起的标识符则var类型与标识符相同
 2.如果expression为函数调用,则var类型为其返回值类型
 3.如果expression为一个左值且用括号括起decltype((xx)) var,则var为指向xx的类型的引用
 4.如果都不满足则var类型与expression类型相同
 */
 double a=10.0;
 int b=10;
 decltype(a*b) c;//c的类型为a*b的类型

   返回类型的后置:当不确定返回类型时可以综合使用auto以及decltype进行返回类型的后置操作:

auto fun1(double,int)->double;//auto此时是一个占位符箭头->后的double是真正的返回类型
template<typename T1,typename T2>
auto fun2(T1 x,T2 y)->decltype(x*y){//利用auto占位同时使用decltype进行推导
 return x*y;
}

4.智能指针

   使用new在堆区动态分配内存时,需要delete释放否则会导致内存泄漏,所以C++引入了智能指针auto_ptr(C++98)来进行管理,智能指针实际上是类对象重载了类如->、[]、等指针常用的运算符,只是表现形式像指针,C++11摒弃auto_ptr并新增提供了三种智能指针unique_ptr、shared_ptr、weak_ptr,auto_ptr、unique_ptr、shared_ptr这三个智能指针模板类都定义了类似指针的对象,可以将new获得的地址赋给这种对象,当智能指针过期时,其析构函数将使用delete来释放内存,同时所有智能指针类中均有explicit防止隐式转化

 #include<memory>//智能指针库
 auto_ptr<Person> ap(new Person("zs",20));//基本使用
 unique_ptr<Person> up(new Person("ls",20));//基本使用
 shared_ptr<Person> sp(new Person("ww",20));//基本使用

   当两个指针指向同一个对象时,指针过期后同一块内存会释放两次即double-free问题,解决可以使用如下几种策略:

  • 重载赋值运算符进行如同深拷贝操作一样的深复制,使其指向不同的对象,其中一个对象为另一个对象的副本
  • 建立所有权概念,对于特定的对象,只能有一个智能指针可以拥有它,这样只有拥有对象的智能指针的析构函数会删除该对象。然后赋值操作转让所有权,这就是用于auto_ptr和unique_ptr的策略,但unique_ptr更为严格,基本不允许进行权限转移。
  • 创建智能更高的指针,跟踪引用特定对象的智能指针技术,称之为引用计数,例如赋值时,计数将加1,而指针过期时,计数将减1,仅当最后一个指针过期时才调用delete。这是shared_ptr采用的策略。

   auto_ptr要求内存的所有权唯一,当前内存块只允许有一个指针指向,当新指针指向时将旧指针释放对内存块的指向,新指针指向该内存块并获得所有权即在有新指针指向内存时,进行所有权转移,无法再访问旧指针。

 //不适合用auto_ptr情况
 auto_ptr<string> as(new string("ddd"));
 auto_ptr<string> as1;
 as1=as;//指针所有权转移
 *as="apple";//旧指针所有权已经转移,无法访问

   此时可使用unique_ptr代替auto_ptr,unique_ptr要求至始至终内存所有权唯一不允许进行权限转移,实现则直接将赋值运算符的重载和构造函数写到私有访问控制下,例如上述as1=as;编译无法通过,所以安全性能高于auto_ptr;同时unique_ptr还支持数组unique_ptr<int[]> upi(new int[5]),当返回临时的右值unique_ptr时,编译器也允许用其他unique_ptr进行接受,但unique_ptr也有缺陷,虽然在设计层面不允许多个智能指针指向同一块内存,但是可以通过代码,让多个智能指针指向同一个内存,此时会出现double-free问题应避免。

int* a=new int;
unique_ptr<int> up1(a);
unique_ptr<int> up2(a);//会出现double-free问题

   一般采用shared_ptr代替auto_ptr进行解决,shared_ptr使用了引用计数的方式,当引用计数为0时才进行析构,很好的解决了该问题,旧指针和新指针都可以使用。需要多个指向同一个对象的指针,选择shared_ptr。
   weak_ptr是一种用于解决shared_ptr相互引用时产生死锁问题的智能指针。如果有两个shared_ptr相互引用,那么这两个shared_ptr指针的引用计数永远不会下降为0,资源永远不会释放。weak_ptr指针通常不单独使用,只能和shared_ptr类型指针搭配使用。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。即使有weak_ptr指向对象,对象也还是会被释放

shared_ptr<CA> sp_ptr1(new CA());
//sp_ptr1.use_count==1;引用计数为1

shared_ptr<CA> sp_ptr2=sp_ptr1;
//sp_ptr1.use_count==2;引用计数为2
//sp_ptr2.use_count==2;引用计数为2

weak_ptr<CA> wk_ptr=sp_ptr1;
//sp_ptr1.use_count==2;引用计数为2,不改变引用计数

5.异常规范

   C++11新增了异常的规范添加了关键字noexcept;不抛出任何异常。void test()noexcept{}原始限制异常抛出类型为void test()throw(int){}不抛出任何异常为void test()throw(){};

6.作用域内枚举

   在传统的枚举中,两个枚举定义的枚举量可能会发生冲突,因为枚举名的作用域为枚举定义所属的作用域,在同一个作用域内定义两个枚举他们的枚举类型不可同名。所以C++11提供了一种新枚举,要求进行显示限定声明枚举类型的作用域,以免发生重名冲突。

enum class Day1{Sunday,Monday,Tuesday};
enum class Day2{Sunday,Monday,Tuesday};
Day1 dd1=Day1::Sunday;
Day2 dd2=Day2::Sunday;

7.对类的修改

   C++11引入explicit不允许进行隐式数据类型转化,必须显示转化。

8.右值引用

   传统C++中仅支持左值的引用,但在C++11中可以新增了右值引用,右值引用使用&&声明int && r=10;但不能对其应用地址运算符。右值包括字符串常量、表达式、临时对象以及返回值不为引用的函数等。右值引用主要用在移动语义上,即将一块内存单元(可以是变量的内存单元也可以是临时对象的内存单元)采用右值引用从一个对象转移到另一个对象,而不是使用深拷贝操作,无需再拷贝一份,大大提升效率,延长临时对象的生存期。例如某个函数返回对象时,寄存器需要有临时对象来接受,此时会调用一次拷贝构造函数(编译器会自动优化),此时该临时需要赋值给新的接受对象则需要再调用一次拷贝构造,效率低下,使用右值引用寄存器中的临时对象进行语义转化,原对象、寄存器中临时对象以及新对象使用的为同一个地址空间,新对象接受时不用调用拷贝构造函数。同时C++11还提供了move();可以将直接左值转化为右值,使用移动构造函数。

class demo {
public:
 demo() :num(new int(0)) {
     cout << "construct!" << endl;
 }
 demo(const demo& d):num(new int(*d.num)){
   cout<<"copy construct"<<endl;
 }
 //移动构造函数
 demo(demo &&d) :num(d.num) {
     d.num = nullptr;//防止临时对象析构当前空间
     cout << "demo move construct!" << endl;
 }
private:
 int *num;
};
demo getDemo(){
return demo();
}
int main() {
 demo a=getDemo();
 demo b;
 demo c=std::move(b);//demo c=b;进行拷贝构造,使用move进行移动构造
 return 0;
}

9.Lambda表达式

   语法格式为:[捕获列表] (参数列表) mutable noexcept/throw() -> 返回值类型{函数体;};

  • 捕获列表能够捕捉上下文中的变量以供Lambda函数使用以及具体使用方式(以值传递的方式或引用传递的方式),[]表示不捕获任何变量、[var]表示值传递方式捕获变量var、[=]表示值传递方式捕获所有父作用域的变量(包括this)、[&var]表示引用传递捕捉变量var、[&]表示引用传递方式捕捉所有父作用域的变量(包括this)、[this]表示值传递方式捕捉当前的this指针、[=, &] 拷贝与引用混合例如[=,&a,&b]表示以引用传递的方式捕捉变量a和b,以值传递方式捕捉其它所有变量;
  • 参数列表可以接收外部传递的多个参数。和普通函数不同的是,如果不需要传递参数,可以连同 () 小括号一起省略;
  • mutable修饰符,默认情况下Lambda函数总是一个const函数,mutable可以取消其常量性。在使用该修饰符时,参数列表不可省略。对于以值传递方式引入的外部变量,lambda 表达式修改的是拷贝的那一份,并不会修改真正的外部变量;
  • 异常说明即你可以使用throw()异常规范来指示lambda表达式不会引发任何异常;
  • -> 返回值类型指明 lambda 匿名函数的返回值类型。如果lambda函数体内只有一个return语句,或者该函数返回void,则编译器可以自行推断出返回值类型,此情况下可以直接省略"->返回值类型";
  • 函数体和普通函数一样,lambda 匿名函数包含的内部代码都放置在函数体中。该函数体内除了可以使用指定传递进来的参数之外,还可以使用指定的外部变量以及全局范围内的所有全局变量。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
   int num[4] = {4, 2, 3, 1};
   vector<int> vi(num,num+sizeof(num)/sizeof(num[0]));
   sort(vi.begin(), vi.end(), [=](int x, int y) -> bool{ return x < y; } );
   for_each(vi.begin(),vi.end(),[](int val){cout<<val<<" ";});
   return 0;
}

10.类型转化

C++11引入四个类型转化运算符:static_cast(静态)、dynamic_cast(动态)、const_cast(常量)、reinterpret_cast(指针、引用与任意类型转化)均类似于强制类型转化,不改变原本的,仅仅生成新的。

  • 使用 static_cast、dynamic_cast 等进行类的指针或引用转化时,实际不会改变原指针的指向,只是生成了新指针,类似于强制类型转化,即原指针使用 static_cast、dynamic_cast 改变指向后访问成员访问的依然是改变前成员(虚函数在改变前已经重写),在接收后则可访问改变后成员。
  • static_cast 用于普通类型转化层次结构中基类和子类之间的指针或引用之间的转化无关系的类之间不可转化。上行转化即将派生类的指针或引用转化为基类表示是安全的,下行转化即将基类的引用或指针转化成派生类表示,无动态类型检查,所以不安全。也可以用于基本类型的转化即将 char 转化为 int 等,需要注意安全,使用语法:static_cast<转化后的类型> (变量)char a=’a’; double b= static_cast<double> (a);
  • static继承关系静态转化,子类->父类(安全):Person* per=new Person; Son* son=new Son; Person* per1=static_cast<Person *>(son)、父类->子类(不安全):Son* son1=static_cast<Son*>(per);向下转化不安全因为有可能父类指针指向的是子类,使用到了多态,所以转化会出错。
  • 动态转化 dynamic_cast 用于层次结构中基类和子类之间的指针或引用之间的转化不可以进行普通的类型以及无关系的类之间转化,上行转化(子类->父类)是安全的所以同 static_cast,下行转化使用 dynamic 具有检查类型的功能,可以检测父类是否使用多态,比 static_cast 安全,父类必须使用了多态才可用 dynamic 转化,否则不可。语法同 static_cast:dynamic_cast<转化后类型>(变量)
  • 常量转化 const_cast:用来进行指向常量的指针类型和引用类型与普通指针、普通引用类型之间的相互转化,不可以应用于普通类型,语法同其他转化:const_cast<转化后类型>(变量)。例如转化指向常量的指针到非常量:const char *p=”apple”; char* p1=const_cast<int*>(p);
  • 重新解释转化reinterpret_cast 最不安全,进行指针、引用与任意类型之间转化:reinterpret_cast <转化后类型>(变量),例:int a=10; int *b=reinterpret_cast<int *>(a);
  • 33
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值