c++11特性

c++11特性

1. auto类型说明符

  • 和只对应一种类型说明符(int,double)不同。auto用于初始化表达式中推断出变量的数据类型。auto定义的变量必须要有初始值

    auto i=1;//自动推断为int型
    auto d=1.0;//自动推断为double
    auto str="hello world";//字符串类型
    auto a;//错误,没有初始化表达式就无法确定a 的类型
    auto it=v.begin();//推断it是一个迭代器
    

auto_ptr智能指针

指向对象的原始指针,在c++中一般在构造函数中进行资源申请,在析构函数中进行资源释放,在c语言中有可能在某函数内部进行资源申请,在函数调用处进行资源释放,资源的申请和释放不再同一处非常容易产生内存泄漏问题。

c++中的动态内存管理是通过new和delete两个操作符来完成的。new操作符,为对象分配内存并调用对象所属类的构造函数,返回一个指向该对象的指针,delete调用时销毁对象,并释放对象所在的内存。

在这里插入图片描述
在这里插入图片描述
智能指针采用引用计数是完成资源管理的常用手法,当引用计数为0时,对象即被销毁

在STL库中对应的实现主要有shared_ptr、weak_ptr两种,二者都是类模板(class template),定义在< memory >头文件里。
二者的“计数”在主流平台上是原子操作,无锁,性能不俗;线程安全性和STL中的string一样。
不同点:

  • shared_ptr控制对象的生命周期。是强引用(想象成铁丝绑住堆上的对象),只要有一个执行x对象的shared_ptr存在,x对象就不会析构。当指向对象x的最后一个shared_ptr析构或reset()的时候,x保证会被销毁。
#include <memory>
#include <iostream>
using namespace std;
int main()
{
	//用make_shared来获得shared_ptr
	shared_ptr<string> p1 = make_shared<string>("");
	//share_ptr可以使用一个new表达式返回的指针进行初始化
	shared_ptr<int> p4(new int(1024));//true
	shared_ptr<int> p5 = new int(1024);//false
	//不能将一个new表达式返回的指针赋值给share_ptr

不要混用new和share_ptr,一旦将一个new表达式返回的指针交给share_ptr管理智慧,就不要再通过普通指针访问这块内存

share_ptr可以通过reset方法重置指向另一个对象,此时原对象的引用计数减一。

//创建一个智能指针
	shared_ptr<int> p1 = make_shared<int>(42);
    cout << *p1 << " "<<endl;//访问
	
	auto p2 = p1;//p1和p2指向同一片内存,且都是智能指针
	cout<<"p1 cnt:"<<p1.use_count()<<"\tp2 cnt:"<<p2.use_count()<<endl;

	p1.reset(new int(404));//通过reset指向另一个对象,此时p2引用次数减一
	cout<<"p1 cnt:"<<p1.use_count()<<"\tp2 cnt:"<<p2.use_count()<<endl;
  • unique_ptr对于所指向的对象,正如其名字所示,是独占的。所以,不可以对unique_ptr进行拷贝、赋值等操作,但是可以通过release函数和reset函数在unique_ptr之间转移控制权。
	cout<<"test unique_ptr base usage:"<<endl;
	unique_ptr<int> up1(new int(1024));
	cout<<"up1: "<<*up1<<endl;
	unique_ptr<int> up2(up1.release());
	//release()返回的指针通常用来初始化另一个智能指针,或给另一个智能指针赋值
	cout<<"up2: "<<*up2<<endl;
	//unique_ptr<int> up3(up1); // wrong, unique_ptr 不支持拷贝
	//up2 = up1; // wrong, unique_ptr 不支持赋值
	unique_ptr<int> up4(new int(1025));
	up4.reset(up2.release());
	cout<<"up4: "<<*up4<<endl;
  • weak_ptr不控制对象的生命周期,是弱引用(想象棉线轻轻拴住堆上的对象),但可以知道对象是否还活着。如果对象还活着,那么它可以提升为有效的shared_ptr,如果对象已经死了,返回一个空的shared_ptr。
  • weak_ptr可以指向shared_ptr所指向的对象,但是却不增加对象的引用计数。这样就有可能出现weak_ptr所指向的对象实际上已经被释放了的情况。因此,weak_ptr有一个lock函数,尝试取回一个指向对象的shared_ptr。

2. decltype类型说明符

  • decltype的作用是选择并返回操作数的数据类型。编译器会分析表达式并得到它的类型,但是不会去计算表达式的值

  • 注意:如果decltype使用的是一个不加括号的变量。得到的就是改变量的类型,如果给变量加上了一层括号,编译器会把它当作一个表达式,得到的则是引用类型。

    int i=10;
    decltype(i) a;//a的类型是int
    decltype((i)) b=i;//b的类型是int&。必须要为其初始化,否则编译错误
    

3. 空指针nullptr

  • nullptr是为了解决原来c++标准中的NULL的二义性问题而引进的一种新的类型,因为NULL实际上代表的是0。但是实际上,用NULL代替0表示空指针在函数重载时会出现问题,程序执行的结果会与我们的想法不同。

    #include <iostream>
    using namespace std;
     
    void func(void* t)
    {
    	cout << "func( void* )" << endl;
    }
     
    void func(int i)
    {
    	cout << "func( int ) " << endl;
    }
     
     
    int main()
    {
    	func(NULL);//func(int)
    	func(nullptr);//func(void*)
    	system("pause");
    	return 0;
    }//在这个函数重载中,我们本来用NULL想要调用void*,但实际上确调用了int,所以引入了nullptr来表示空指针
    #define NULL((void*)0)//在c语言中把空指针赋给指针的时候,发生了隐式类型转换,把void指针转换成了相应类型的指针
    #define NULL 0//c++是强类型语言,void*不能隐式转换为其他类型指针,所以在这里会有歧义
    

4. 基于范围的for循环

  • 基于范围的for循环:简化了常见的循环,对数组或容器类的每个元素执行相同的操作

    int age[10]={1,2,3,4,5,6,7,8};
    for(int y:age)//第一部分是范围内用于迭代的变量,第二部分表示被迭代的范围
    {//迭代器可以指向容器中的某个元素,通过迭代器就可以读写它指向的元素。从这一点上看,迭代器和指针类似
        cout<<y<<endl;
    }
    
  • for循环迭代的范围必须是确定的

    //当数组作为函数参数时,数组名退化为指针,不能使用范围for
    void func(int arr[],int n)
    {
        for(const auto& e:arr)
        {
            cout<<e<<" ";
        }
        cout<<endl;
    }
    

5. 扩展的sizeof

  • 在c++98中,只有静态成员,或者对象的实例才能对其成员进行sizeof操作,但是在c++11中可以。在c++11中,对非静态成员变量使用sizeof操作时合法的。
  • sizeof是一个操作符,其作用是返回一个对象或类型所占内存字节数。其返回值类型为size_t。(size_t在头文件stddef.h中定义,它依赖于编译系统的值,一般定义为 typedef unsigned int size_t;

size() sizeof() strlen() str.length()的区别

数组或字符串的长度:sizeof()、strlen()

  1. sizeof():返回所占总空间的字节数
  2. strlen():返回字符数组或字符串所占的字节数参数必须是字符型指针(char*)

sizeof(…)是运算符,其值在编译时即计算好了,参数可以是数组、指针、类型、对象、函数等。它的功能是:获得保证能容纳实现所建立的最大对象的字节大小。由于在编译时计算,因此sizeof不能用来返回动态分配的内存空间的大小。

strlen(…)是函数,要在运行时才能计算。参数必须是字符型指针(char*)。当数组名作为参数传入时,实际上数组就退化成指针了。它的功能是:返回字符串的长度。该字符串可能是自己定义的,也可能是内存中随机的,该函数实际完成的功能是从代表该字符串的第一个地址开始遍历,直到遇到结束符’\0’。返回的长度大小不包括’\0’。

c/c++ strlen(str)和str.length()和str.size()都可以求字符串长度。
其中str.length()和str.size()是用于求string类对象的成员函数
strlen(str)是用于求字符数组的长度,其参数是char*。

6. 虚函数的override和final指示符

  • override,表示应当重写基类中的虚函数
  • final,表似乎派生类不应当重写这个虚函数

7. 限定作用域的枚举

enum class,在枚举类型的作用域外是不可访问的,相反,在不限定作用域的枚举类型中,枚举成员的作用域与枚举类型本身的作用域相同。

枚举作用域(enumeration scope)是指枚举类型的成员的名字的作用域,起自其声明之处,终止枚举定义结束之处

  • 枚举元素是常量,不能对他们赋值
    例如:

    enum Weekday{SUN,MON,TUE,SAT};
    SUN=0;//错误,不能写赋值表达式:
    
  • 枚举元素有默认值,依次为:0,1,2,…

  • 也可以在声明时另行指定枚举元素的值
    如:

    enum Weekday{SUN=7,MON=1,TUE,SAT};
    
  • 枚举值可以进行关系运算
    不能直接用一个整数给枚举值赋值,需要进行强制类型转化。

不限定作用域的枚举类型可能导致枚举量泄漏到所在的空间作用域

//在同一个作用域中,定义了不限定作用域的枚举类Color,然后定义了red变量。由于没限定作用域,所以外部也可以使用red。auto red重新定义了red所以报错。
namespace TestSpace {
enum Color {
  red = 0,
  green,
  blue,
};
auto red = true;  // 错误
}; // namespace TestSpace

限定作用域的枚举类型

namespace TestSpace {
enum class Color {
  red = 0,
  green,
  blue,
};
auto red = true; // 没问题
}; // namespace TestSpace

8. constexpr变量

  • constexpr表达式是指值不会改变,并且在编译过程中就能得到计算结果的表达式。声明为constexpr变量一定是一个const变量,而且必须用常量表达式初始化
constexpr int mf = 20;  //20是常量表达式
constexpr int limit = mf + 1; // mf + 1是常量表达式
constexpr int sz = size(); //之后当size是一个constexpr函数时才是一条正确的声明语句
  • 在constexpr声明中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指的对象无关

  • const只能保证在运行时是常量,即具有运行时数据的不可更改性

9. noexcept

  • 在c++11中,声明一个函数不可以抛出任何异常使用关键字noexcept
  • 编译器不会在编译时检查noexcept的说明,如果一个函数在说明了noexcept的同时又含有throw语句,编译器将顺利编译通过,并不会因为这种违反异常说明的情况而报错。
void fun1(int) noexcept;//不会抛出异常
void fun2(int);//可能会抛出异常
void fun3(int) noexcept(true);//不会抛出异常
void fun4(int) noexcept(false);//可能会抛出异常

10. 匿名函数lambda表达式

lambda表达式具体形式如下:

[capture](parameters)mutable exception->return-type{body};

/*1)capture list:捕获外部变量列表
2)params list:形参列表
3)mutable:标明是否可以修改捕获的变量,默认情况下,Lambda函数总是一个const函数
mutable可以取消其常量性。在使用该修饰符时,参数列表不可省略(参数为空)
4)exception: 异常设定
5)return type:返回类型
6)function body:函数体*/


[](int x,int y){return x+y;}//隐式返回类型
[](int &x){++x}//没有return语句,lambda函数的返回类型是void
[](){++global_x;}//没有参数,仅仅访问某一个全局变量
[]{++global_x;}//与上一个相同,省略了()
[](int x,int y)->int{int z=x+y;return z;}//显示指定返回类型

正常使用中,我们很少会用到return type,因为Lambda表达式可以根据function body中的返回值来判断返回类型。省略params list时,也就相当于一个普通的无参函数。

符号含义
[]不截取任何变量
[&]截取外部作用域中所有变量,并作为引用在函数体中使用
[=]截取外部作用域中所有变量,并拷贝一份在函数体中使用
[=,&foo]截取外部作用域中所有变量,并拷贝一份在函数体中使用,但是对foo变量使用引用
[bar]截取bar变量并且拷贝一份在函数体重使用,同时不截取其他变量
[x,&y]x按值传递,y按引用传递
[this]截取当前类中的this指针。如果已经使用了&或者=就默认添加此选项。

类型转换static_cast/dynamic_cast/const_cast/reinterpret_cast

参考文章:类型转换

  • 在C语言中强制类型转换写成(new_type_name) expression的形式,new_type_name 是要转换的目标类型,expression 是待转换的表达式
  • 在C++中强制类型转换通过更明显的关键字来完成,分别是static_cast、 dynamic_cast, const_cast、 reinterpret_cast

static_cast 是静态转换,在编译期完成完成转换,与C语言中的强制类型转换重合度最高

它不能转换掉表达式的 const、volitale 或者 __unaligned 属性。

    int val = 110119;
    char c = static_cast<char>(val);
    double d = static_cast<double>(val);

dynamic_cast 是动态转换,在运行时转换会进行检查,必须用在有继承关系的多态结构中

只能对指针和引用的进行转换,并且只用于类继承结构中基类和派生类之间指针或引用的转换,可以进行向上、向下,或者横向的转换。
必须有继承关系的类之间才能转换,并且在基类中有虚函数才可以

   struct B { virtual void test() {} };
   struct D1 : virtual B { };
   struct D2 : virtual B { };
   struct MD : D1, D2 { };


   D1* pd1 = new MD();
   std::cout << pd1 << std::endl;

   // 向上转型
   B* pb = dynamic_cast<B*>(pd1);
   std::cout << pb << std::endl;

   // 向下转型
   MD* pmd = dynamic_cast<MD*>(pd1);
   std::cout << pmd << std::endl;

   // 横向转型
   D2* pd2 = dynamic_cast<D2*>(pd1);
   std::cout << pd2 << std::endl;

const_cast 是常量转换,用于取出指针或引用的常量属性,但是尽量通过设计杜绝它的使用场景

但需要特别注意的是 const_cast 不能去除变量的常量性,只能用来去除指向常数对象的指针或引用的常量性,且去除常量性的对象必须为指针或引用。
常量指针被转化成非常量指针,并且仍然指向原来的对象,常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象可能被转换成非常量对象

reinterpret_cast 是一种内存数据的重新解释,比较原始,开发者使用它的时候应该明确的知道自己在做什么

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值