每日一面——C++11的新特性

写前声明:参考链接 C++面经面试宝典

一、C++11有哪些新特性?

(1)语法的改进

  • 统一的初始化方式——使用初始化列表
  • 成员变量默认初始化
  • auto关键字用于定义变量,编译器自动判断类型
  • decltype求表达式的类型
  • 智能这种 shared_ptr
  • 空指针
  • 基于范围的 for循环
  • 右值引用和 move语义,使之有意识的减少深拷贝操作

(2)标准库扩充

  • 无序容器(哈希表)与 map一样,但效率更高
  • 正则表达式
  • Lambda 表达式

二、说一说了解的Lambda函数的全部知识

  • 利用它可以飙血内嵌的匿名函数,用于替换独立函数或者函数对象
  • 每当定义一个 Lambda表达式后,编译器会自动生成一个匿名类(这个类当然重载了()运算符,我们称为闭包类型)。在运行时,这个Lambda表达式就会返回一个匿名的闭包实例,是一个右值。闭包的一个强大之处是其可以通过传值或者引用的方式捕捉其封装作用域内的变量,前面的方括号就是用来定义捕捉模式以及变量,我们又将其称为lambda捕捉块。
  • Lambda 表达式的语法定义如下:[capture] (parameters) mutable ->return-type {statement};
  • 使用位置返回来指定返回类型
  • 可以忽略参数列表和返回值类型,但必须包含捕获列表和函数体

三、C++中的NULL和nullptr的区别?

在C++中指针有明确的类型定义,但是将NULL定义为0带来的另一个问题是无法与整数0区分

由于在传入NULL参数时,会把它看作整数0,如果我们想调用参数是指针的函数,该怎么办? nullptr在C++11被引入,它可以明确区分整型和指针类型,能够根据环境自动转换成相应的指针类型,但不会被转换为任何整型,所以不会造成参数传递错误

四、auto、decltype和 decltype(auto)的用法

1)auto
auto用于定义变量,编译器可以自动判断变量的类型。auto 让编译器通过初始值来进行类型推演。从而获得定义变量的类型,所以说 auto 定义的变量必须有初始值。使用auto定义迭代器比较方便

auto 和 const 的结合使用

  • 当类型不为引用时,auto 的推导结果将不保留表达式的 const 属性
  • 当类型为引用时,auto 的推导结果将保留表达式的 const 属性

2)decltype
有的时候我们还会遇到这种情况,我们希望从表达式中推断出要定义变量的类型,但却不想用表达式的值去初始化变量。还有可能是函数的返回类型为某表达式的值类型。在这些时候auto显得就无力了,所以C++11又引入了第二种类型说明符decltype,它的作用是选择并返回操作数的数据类型。在此过程中,编译器只是分析表达式并得到它的类型,却不进行实际的计算表达式的值

3)decltype(auto)
decltype(auto)是C++14新增的类型指示符,可以用来声明变量以及指示函数返回类型。在使用时,会将“=”号左边的表达式替换掉auto,再根据decltype的语法规则来确定类型

五、说说C++中的智能指针和指针的区别是什么?

智能指针和普通指针的区别在于智能指针实际上是对普通指针加了一层封装继承,它自动负责释放所指对象,使得智能指针可以方便的管理一个对象的生命周期

六、使用智能指针的原因?

申请的空间(即new出来的空间),在使用结束时,需要delete掉,否则会形成内存碎片。在程序运行期间,虽然我们可以在析构函数中delete掉我们new出来的对象,但这也并不能解决所有问题。使用智能指针可以很大程度上避免这个问题,因为智能指针就是一个类,当超出了类的作用域时,类会自动调用析构函数,析构函数会自动释放资源。所以,智能指针的作用原理就是在函数结束时自动释放内存空间,避免了手动释放内存空间。

七、四种智能指针的特性

(1)auto_ptr
拷贝构造和赋值重载做的都是浅拷贝,不推荐

(2)unique_ptr
unique_ptr 是独享所有权的智能指针,保证同一实际内资源只能被一个指针占有,该指针不能构造和赋值,但可以进行移动构造和移动赋值构造(调用move())函数

(3)shared_ptr
shared_ptr 实现共享式拥有概念,多个智能指针可以指向相同对象,该对象和其相关资源会在 “最后一个引用被销毁” 时释放,从名字 share 可以看出了资源可以被多个指针共享,它使用引用计数机制来表明被几个指针共享。通过 use_count()来查看资源共享者个数,当我们调用 release() 时,当前指针会释放资源所有全,引用计数减一,当计数为0时,资源会被释放。shared_ptr 是为了解决 auto_ptr 在对象所有权上的局限性(auto_ptr 是独占的), 在使用引用计数的机制上提供了可以共享所有权的智能指针。

(4)weak_ptr
weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象。进行该对象的内存管理的是那个强引用的 shared_ptr。weak_ptr只是提供了对管理对象的一个访问手段。weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作,它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用计数的增加或减少。weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化。shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。

八、使用智能指针管理内存资源,RAII是怎么回事?

  • RAII全称是“Resource Acquisition is Initialization”,直译过来是“资源获取即初始化”,也就是说在构造函数中申请分配资源,在析构函数中释放资源
  • 因为C++的语言机制保证了,当一个对象创建的时候,自动调用构造函数,当对象超出作用域的时候会自动调用析构函数。所以,在RAII的指导下,我们应该使用类来管理资源,将资源和对象的生命周期绑定
  • 智能指针(std::shared_ptr和std::unique_ptr)即RAII最具代表的实现,使用智能指针,可以实现自动的内存管理,再也不需要担心忘记delete造成的内存泄漏

九、智能指针的交叉引用问题?

交叉引用是指使用多个智能指针时,出现了指针直接相互指向,从而形成环的情况,这种情况下,智能指针往往不能正常调用对象的析构函数,从而造成内存泄漏。为了解决循环引用导致的内存泄漏,引入了弱指针weak_ptr,weak_ptr的构造函数不会修改引用计数的值,从而不会对对象的内存进行管理,其类似一个普通指针,但是不会指向引用计数的共享内存,但是可以检测到所管理的对象是否已经被释放,从而避免非法访问。

十、weak_ptr 能不能知道对象计数为 0,为什么?

weak_ptr是一种不控制对象生命周期的智能指针,它指向一个shared_ptr管理的对象。进行该对象管理的是那个引用的shared_ptr。weak_ptr只是提供了对管理 对象的一个访问手段。weak_ptr设计的目的只是为了配合shared_ptr而引入的一种智能指针,配合shared_ptr工作,它只可以从一个shared_ptr或者另一个weak_ptr对象构造,它的构造和析构不会引起计数的增加或减少。

十一、简述 C++ 右值引用与转移语义

  • 右值引用

一般来说,不能取地址的表达式,就是右值引用,能取地址的,就是左值。

  • 转移语义

move 本意为 “移动”,但该函数并不能移动任何数据,它的功能很简单,就是将某个左值强制转化为右值。基于move() 函数特殊的功能,其常用于实现移动语义

十二、说一下C++左值引用和右值引用

C++11正是通过引入右值引用来优化性能,具体来说是通过移动语义来避免无谓拷贝的问题,通过move语义来将临时生成的左值中的资源无代价的转移到另外一个对象中去,通过完美转发来解决不能按照参数实际类型来转发的问题(同时,完美转发获得的一个好处是可以实现移动语义)。

① 在C++11中所有的值必属于左值、右值两者之一,右值又可以细分为纯右值、将亡值。在C++11中可以取地址的、有名字的就是左值,反之,不能取地址的、没有名字的就是右值(将亡值或纯右值)。

② C++11对C++98中的右值进行了扩充。在C++11中右值又分为纯右值(prvalue,Pure Rvalue)和将亡值(xvalue,eXpiring Value)。其中纯右值的概念等同于我们在C++98标准中右值的概念,指的是临时变量和不跟对象关联的字面量值;将亡值则是C++11新增的跟右值引用相关的表达式,这样表达式通常是将要被移动的对象(移为他用),比如返回右值引用T&&的函数返回值、std::move的返回值,或者转换为T&&的类型转换函数的返回值。

③ 左值引用就是对一个左值进行引用的类型。右值引用就是对一个右值进行引用的类型,事实上,由于右值通常不具有名字,我们也只能通过引用的方式找到它的存在。右值引用和左值引用都是属于引用类型。无论是声明一个左值引用还是右值引用,都必须立即进行初始化。而其原因可以理解为是引用类型本身自己并不拥有所绑定对象的内存,只是该对象的一个别名。左值引用是具名变量值的别名,而右值引用则是不具名(匿名)变量的别名。但常量左值引用是个“万能”的引用类型。它可以接受非常量左值、常量左值、右值对其进行初始化。不过常量左值所引用的右值在它的“余生”中只能是只读的。相对地,非常量左值只能接受非常量左值对其进行初始化。

④ 右值值引用通常不能绑定到任何的左值,要想绑定一个左值到右值引用,通常需要std::move()将左值强制转换为右值。

左值和右值

  • 左值:表示的是可以获取地址的表达式,它能出现在赋值语句的左边,对该表达式进行赋值。但是修饰符const的出现使得可以声明如下的标识符,它可以取得地址,但是没办法对其进行赋值
  • 右值:表示无法获取地址的对象,有常量值、函数返回值、lambda表达式等。无法获取地址,但不表示其不可改变,当定义了右值的右值引用时就可以更改右值。
    左值引用和右值引用

左值引用: 传统的C++中引用被称为左值引用

右值引用: C++11中增加了右值引用,右值引用关联到右值时,右值被存储到特定位置,右值引用指向该特定位置,也就是说,右值虽然无法获取地址,但是右值引用是可以获取地址的,该地址表示临时对象的存储位置

这里主要说一下右值引用的特点:

  • 特点1:通过右值引用的声明,右值又“重获新生”,其生命周期与右值引用类型变量的生命周期一样长,只要该变量还活着,该右值临时量将会一直存活下去

  • 特点2:右值引用独立于左值和右值。意思是右值引用类型的变量可能是左值也可能是右值

  • 特点3:T&& t在发生自动类型推断的时候,它是左值还是右值取决于它的初始化。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

leisure-pp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值