面试总结:C++11新特性

对于C++11的特性你了解多少?简单说说

- 在语法层面引入统一初始化(即列表初始化),那么C++11的初始化就可以分为列表初始化和字面值初始化

列表初始化就是使用{}(花括号)来进行对象、内置基本类型等的初始化
int d = 1;
int a = {d};
但是这种初始化方法需要确保类型安全和精度不丢失(我们知道,相比于C语言,C++是一种类型安全的编程语言,有着严格的类型检查机制)

比如说,这种情况C++17以后会报错

//error
double d = 3.1415926;
int a = {d};

这是由于这段代码试图将一个double类型的变量用于初始化一个int类型的变量,这是类型不安全的,同时,int 占用4字节,而double占用8字节,这是有精度丢失的风险的

像这种大精度转小精度,大整型转小整型又被称为窄化转换,在C++的列表初始化中是不允许的,由于精度丢失风险和类型不安全等问题出警告或是直接报编译错误

需要说明的是例如:

double d = 3.1415926;
int a = d;

//像字面值初始化(即直接赋值这种初始化形式)
//C++编译器会默认发生内置基本类型的隐式转换,这
//时候不会因为类型不安全报错,但是精度是会丢失的

- 类的成员变量默认初始化

我们知道,static修饰的内置数据类型的成员变量会默认初始化为0,但是对于非static成员变量来说,类内的内置数据类型成员变量(char,short,int等),如果没有显式初始化,仍然会初始化一个不定值;类内的对象类型成员变量,如果没有显式初始化,就会调用默认构造函数

- auto关键字和decltype进行数据类型推导

auto关键字借助编译器进行数据类型推导,decltype可以对变量或表达式进行数据类型推导。

实际上,二者都是C++11中的类型推导方式,但是使用方式和推导规则有所不同

使用方式角度:
auto关键字:编译器通过表达式的值来推断auto关键字应该对应什么数据类型
decltype: 是通过表达式本身来推断数据类型,而不是值

int a = 0;
auto d = a;            // int d =a;
decltype(a) d = a;     // int d =a;

//类型推导一般用在类型复杂,关系不好辨认的情况下,
//如果是正常情况下一般不建议增加不必要开销

//比如给一个例子:
auto lam = [](int a,int b)->int{return a+b};
auto res = lam(1,2);
//像这种情况,用lambda匿名表达式作为一个变量传递的时候

- 智能指针(4种智能指针 auto_ptr,unique_ptr,share_ptr和weak_ptr

auto_ptr是一种已经被弃用的智能指针,作用其实和unique_ptr差不多,被弃用的主要原因是auto_ptr存在一些问题。
我们知道,智能指针是RALL技术的一种应用。RALL:资源获取即初始化,这个技术是借用类的生命周期来管理动态内存的申请与释放。在创建RALL类对象时调用构造函数托管资源,对象生命周期结束调用析构函数时释放动态内存空间,达到一个对动态内存分配和回收的自动化处理。

那么在使用RALL技术在智能指针对象中托管资源时,我们往往不希望看到多个指针指向同一个对象,这样会有double free的问题
这个情况类似于:

String * strPtr = new String("data");
auto_ptr<String> ap1(strPtr);
auto_ptr<String> ap2(ap1);

//假设后续对ap1进行操作,会发生段错误

本处首先是动态内存分配构造一个自定义的类String,使用String类的有参构造函数,在堆上分配空间,然后创建一个auto_ptr智能指针,在构造智能指针对象ap1时托管资源,也就是把strPtr指向的内存空间纳入托管,后续又定义了一个auto_ptr对象ap2,使用已存在的ap1对象去初始化ap2。

这时候按理来说是调用拷贝构造函数,但是auto_ptr的拷贝构造函数的参数传入的不是常规的const &,而是&类型,由于不希望多个指针同时指向托管对象的double free问题,这里的拷贝构造函数不是一个深拷贝的复制,而是在内部悄悄做了一个移动语义的操作,也就是直接把ap2的资源指针指向ap1的资源指针指向的位置,然后将ap1的指向赋值为NULL。因此,就有了假设后续对ap1进行操作,会发生段错误的问题。

这时候,unique_ptr作为auto_ptr的替代被提出来了,unique_ptr是一个独占所有权的指针,也就是说,unique_ptr在实现上避免了多个指针对象指向同一个托管资源的问题。主要的做法就是禁用了拷贝构造和赋值运算符函数(需要说明的是,在C++11之前,禁用拷贝构造和赋值运算符函数一般可以使用private访问权限声明的办法,在C++11以后,直接在形参后 ”=delete“即可)

但是有人可能要问禁用了拷贝构造和赋值运算符函数的话会不会使得智能指针无法放入vector这些容器中了,当然不会,虽然拷贝构造和赋值运算符函数被禁用,但是移动语义的相关函数没有被禁止,也就是说:使用std::move将左值的智能指针对象转为右值,优先使用移动语义即可

String * strPtr = new String("data");
unique_ptr<String> ap3(strPtr);
vector<unique_ptr<String>> vec;
vec.push_back(ap3);  //error 拷贝构造和赋值运算符函数被禁用
vec.push_back(std::move(ap3));   //使用移动拷贝构造

share_ptr:
在某些使用场景下,unique_ptr并不是完美适配,比如多线程共享的资源,这个资源本身就可以被多个线程共享,无需独占。这时候share_ptr,这样一个共享所有权的智能指针就被提出来了,每次托管资源时都在计数器上+1,释放对应资源时-1,计数器值为0时意味着当前托管的资源将被释放

在这里插入图片描述
share_ptr对象分为两部分,资源指针管理和计数器控制块,资源指针部分多个对象同时指向同一片托管的资源,并且多个对象的计数器控制块也同时指向堆上一个定义开辟出来的计数器。share_ptr资源指针管理时,并非线程安全,计数器的控制操作是线程安全的

share_ptr有一个循环引用的问题,也就是
在这里插入图片描述
假设当前栈区有share_ptr asp和share_ptr bsp,他们又各自托管了A类对象和B类对象。A类对象内部有一个share_ptr _b指针,B类对象内部有一个share_ptr _a指针,这时候假设_b指针和_a指针分别指向对方的对象空间,也就是说当前有两个share_ptr智能指针指向同一个A类对象,有两个share_ptr智能指针指向同一个B类对象,引用计数都为2。在某个时刻,asp和bsp的生命周期到达,编译器自动调用他们的析构函数,由于共享指针的特性,这时候引用计数2 - 1 = 1,并未释放托管的资源。后续A,B对象将死锁,一直无法释放。

为了解决这个循环引用的问题,我们又引入了weak_ptr,这个弱引用的智能指针托管资源时将不会改变引用计数,想一下,假如A,B对象里面是weak_ptr(weak_ptr _b 和 weak_ptr _a )是不是在栈区的asp和bsp生命周期到达时托管的对象也跟着释放了。(weak_ptr使用lock()方法可以暂时提升权限为share_ptr,通常用来查看这个share_ptr是否已经被释放了)

- 右值引用和移动语义

左值和右值:
一般我们认为左值就是可以取地址的值,右值是不能取地址的值
左值是有确切地址的,右值只是一个临时的变量

//左值
&"hello";   //显式的字符串是左值
&++i;       //前置++返回的是自身的引用,不是临时对象
int * p = nullptr;
&p;         //指针变量是左值

//右值
&i++;  //error 后置++返回的是临时对象,内部调用了前置++对自身做了一个 ++*this
&std::move(p); //error std::move(p)把左值p转为右值

在使用拷贝构造和赋值运算符函数时,我们通常是一个深拷贝的写法:

class String{
public:
     //构造和析构之类的实现略过了奥
     String();
     String(const char * str);
     ~String();
     
     String(const String & object){
        int length = strlen(object._data);
        _data = new char[length+1];
        strcpy(_data,object._data);
     }
private:
    char * _data;  //资源指针     
}

由于使用了const &作为形参传递,无论是左值还是右值,由于引用折叠的特性,到最后都可以成功调用拷贝构造函数,例如下面的实现:这里就是先调用有参构造,创建了一个临时String对象,然后作为参数传入vector的push_back方法,符合拷贝构造调用的时机,调用拷贝构造函数

vector<String> vec;
vec.push_back(new String("hello"));

但是我们发现,如果是对于临时对象使用深拷贝是一件意义不大的事情,因为临时对象的生命周期很短,对应的内存空间要在短时间内分配和回收,假如有大量的临时变量做拷贝构造,开销将会很不理想。这时候如果采用移动语义,将临时对象的内存空间直接由初始化的对象资源指针指向,临时对象的资源指针直接为NULL,就无需额外申请内存空间了(但是这种移动语义的处理只能对右值来做,对左值意义不大)

在这里插入图片描述
(图源百度,包浆的好图咯)

但是问题来了,拷贝构造函数里面的const &参数并不能识别出谁是左值,谁是右值,这时候就无法针对右值来做一个移动语义的处理。C++11引入了一个右值引用,类似于 int && ref = std::move(p);右值引用只能绑定右值,我们只需要实现移动拷贝构造和移动赋值函数,这时候,如果传入临时对象时,就会优先调用移动语义函数

String(String && str){
    _data = str._data;
    str._data = nullptr;
}

String & operator=(String && str){
    if(this != & str){
       delete[] _data;
       _data = nullptr;
       _data = str._data;
       str._data = nullptr;
    }
    return *this;
}

需要注意的是,右值引用不一定是左值

//左值ref
int  p = 0;
int && ref = std::move(p);

//若果函数返回值为右值引用
int && getData(){
    int p = 0;
    return std::move(p);
}

&getData();  //error 返回值是右值引用,也就是说,右值引用不一定是左值

- 范围for循环

范围性的迭代写法

vector<int> vec(n,0);
for(auto i : vec){
   //范围for循环:循环体内的i为vec容器的各个元素的拷贝
}
for(auto & i : vec){
   //范围for循环:循环体内的i为vec容器的各个元素的引用,可以修改
}

- 完美转发(std::forward

函数模板的参数传递的时候参数的左值右值属性传入函数体内部不改变
主要是使用了std::forward()完成实现
在这里插入图片描述
std::forward()的底层就主要是做了一个static_cast 转换为右值引用,结合函数模板万能引用(使得C++函数既能接受左值又可以接受右值)的知识理解:

templete<type_name T>
void Func(T && a){
    return;
}

int data = 10;
Func(1123);  //函数模板中T 理解为 int &&
Func(data);  //函数模板中T 理解为 int &

在这里插入图片描述

- 空指针nullptr

在C语言中,NULL用于表示 ((void *)0) ,此时C语言中为指针赋值NULL,实际上是做了一个下行转换,把void *自动转换为其他的数据类型了,而且需要注意的时C语言中的NULL是一个宏定义

在C++中,NULL只用于表示0,这是因为C++中的void *不能随意转换为其他数据类型,(也即C++不支持void *的隐式数据类型转换)只能把NULL表示为0来使用,但是这个时候又会出现新的问题,假如有:

void func(int p){}
void func(int * p){}

int * p = NULL;
func(p);  //这个时候会产生二义性,由于NULL等同于0,在上述重载中有二义性的问题

所以C++11中引入了nullptr,表示空指针,nullptr可以像C语言的void *一样可以随意转换为其他数据类型

- lambda表达式(允许在代码中定义匿名函数)

lambda表达式 [ “捕获列表” ] ( “形参列表” )->typename{ “函数体” }

lambda表达式相当于是一个未命名类的未命名对象,在代码中定义匿名函数,尤其是在和STL函数配合时有很好的效果

vector<int> vec;
sort(vec.begin(),vec.end(),[](const int & a,const int & b)->bool{return a > b});

//lambda匿名函数的返回值不一定要说明,可以由编译器自己推断

同时lambda表达式可以作为一个变量传递

auto lam = [](const int & a,const int & b)->bool{return a > b};
lam(1,2);

- 无序哈希表,如unordered_map这些

像unordered_map,unordered_set这些都是哈希表为底层实现的容器,查找的效率为O(1),当然由于哈希冲突等问题,在某些情况下最坏时间复杂度时O(n)

像map,set这些序列化容器的底层则是由红黑树实现的,所谓红黑树就是不严格平衡的二叉搜索树,其中:根节点和外部节点为黑色,不能有连续的两个红色节点,所有根节点到外部节点的路径上黑色节点的数目一致,搜索增删的时间复杂度为O(logn)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/489977.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

超全整理,软件测试-性能测试流程汇总,看这一篇就够了...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 性能测试&#xf…

这个插件,提供了1000多个在线底图服务!

本文推荐一下QGIS中的热门插件:QuickMapService。目前在QGIS插件市场下载量排名第一,先看下官网的介绍: Easy to use list of services and search for finding datasets and basemaps. 言简意赅,用来添加QGIS底图的插件。 插件安装 打开QGIS自带的插件管理器。 在搜索框中…

学习要不畏难

我突然发现&#xff0c;畏难心是阻碍我成长的最大敌人。事未难&#xff0c;心先难&#xff0c;心比事都难&#xff0c;是我最大的毛病。然而一念由心生&#xff0c;心不难时&#xff0c;则真难事也不再难。很多那些自认为很难的事&#xff0c;硬着头皮做下来的时候&#xff0c;…

黑马鸿蒙学习(3):滑动条

1&#xff09; 滑动条slidebar属性&#xff1a;

MySQL-1.数据库的基本操作

1. 数据库的基本操作 show databases; information_schema&#xff1a;信息图式&#xff0c;存储服务器管理数据库的信息 mysql&#xff1a;存放系统信息&#xff0c;用户名密码等 performance_schema&#xff1a;性能图式 sys&#xff1a;系统文件 1.1 创建数据库-studen…

[STL]priority_queue类及反向迭代器的模拟实现

&#x1fa90;&#x1fa90;&#x1fa90;欢迎来到程序员餐厅&#x1f4ab;&#x1f4ab;&#x1f4ab; 今日主菜&#xff1a; priority_queue类及反向迭代器 主厨&#xff1a;邪王真眼 主厨的主页&#xff1a;Chef‘s blog 所属专栏&#xff1a;c大冒险 向着c&…

【Web应用技术基础】HTML(5)——案例1:展示简历信息

样式&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>展示简历信息…

【微服务】Gateway

文章目录 1.基本介绍官方文档&#xff1a;https://springdoc.cn/spring-cloud-gateway/#gateway-starter1.引出网关2.使用网关服务架构图3.Gateway网络拓扑图&#xff08;背下来&#xff09;4.Gateway特性5.Gateway核心组件1.基本介绍2.断言3.过滤 6.Gateway工作机制 2.搭建Gat…

从姿态估计到3D动画

在本文中&#xff0c;我们将尝试通过跟踪 2D 视频中的动作来渲染人物的 3D 动画。 在 3D 图形中制作人物动画需要大量的运动跟踪器来跟踪人物的动作&#xff0c;并且还需要时间手动制作每个肢体的动画。 我们的目标是提供一种节省时间的方法来完成同样的任务。 我们对这个问题…

超实用!10条JavaScript这20年来增加的新功能?

部门捞人&#xff1a;前端可投&#xff1a;OD软件工程师社会招聘-表单-金数据 在过去的20年里&#xff0c;JavaScript经历了多次更新和升级&#xff0c;引入了许多新功能以增强其表达力、交互性和开发效率。以下是一些显著的新功能&#xff1a; 1.ECMAScript 6 (ES6) &#xf…

【ssh连接】奇奇怪怪报错记录

gitlab配置ssh连接&#xff0c;先跟着教程生成密钥&#xff0c;上传公钥&#xff0c;将服务器信息存入config文件&#xff0c;但是ssh连接超时&#xff0c;很急&#xff0c;想用服务器&#xff0c;各种搜索尝试&#xff0c;搞了两三天别的什么都没干&#xff0c;还是没解决&…

vue脚手架创建项目:账号登录(利用element-ui快速开发)(取消eslint强制格式)(修改端口号)

新手看不懂&#xff0c;老手不用看系列 文章目录 一、准备工作1.1 取消强制格式检查1.2 导入依赖&#xff0c;注册依赖 二、去element-ui官网找样式写Login组件2.1 引用局部组件2.2 运行项目 三、看一下发现没问题&#xff0c;开始修改前端的代码四、修改端口号4.1 修改后端端口…

中国赛道领跑之争:安踏将耐克越甩越远

一双鞋、一件衣服每被穿一次&#xff0c;消费者就会把它背后的品牌和自身的体验联系起来&#xff0c;做出评判。所以&#xff0c;如果说有什么领域能充分展示国产品牌的发展进步&#xff0c;鞋服一定包含在内&#xff0c;尤其是强调专业性的体育运动市场。 一年前的2023年3月&…

CSS3语法及选择器总结

CSS定义 css是一种样式表语言&#xff0c;用来美化HTML文档 一.CSS的引用 方式一:内部样式表(HTML中引用) 在HTML的title标签下方添加style双标签&#xff0c;style标签里面书写CSS *style里面的注释为/ * … / CSS的书写规则: 选择器{属性名&#xff1a;属性值&#xff…

Vue+Element-UI el-table表格根据指定条件动态合并行span-method

当涉及到展示复杂数据的表格时&#xff0c;Element UI 中的 el-table 是一个非常有用的组件。在某些情况下&#xff0c;可能需要对表格进行合并显示以提高可读性。 案例需求&#xff1a;页面中有个表格组件&#xff0c;其中包含了一些学生的姓名、年级、负责班级和科目成绩等信…

20240319-2-机器学习基础面试题

⽼板给了你⼀个关于癌症检测的数据集&#xff0c;你构建了⼆分类器然后计算了准确率为 98%&#xff0c; 你是否对这个模型很满意&#xff1f;为什么&#xff1f;如果还不算理想&#xff0c;接下来该怎么做&#xff1f; 首先模型主要是找出患有癌症的患者&#xff0c;模型关注的…

水离子雾化壁炉的设计特点与优势

水离子雾化壁炉具有许多独特的设计特点和优势&#xff0c;使其在家居装饰和氛围营造方面具有吸引力。以下是其主要设计特点和优势&#xff1a; 仿真火焰效果&#xff1a; 水离子雾化壁炉采用超声波雾化技术将水分子雾化成微细的水离子&#xff0c;然后通过风扇吹出再经过UVC紫…

Binary Search Tree

这篇博客要说的是二叉搜索树&#xff0c;又叫二叉排序树&#xff0c;它或者是一颗空树&#xff0c;或者是具有以下性质的二叉树&#xff1a; 若它的左子树不为空&#xff0c;那么左子树上所有节点的值都小于根节点的值&#xff0c;不会出现等于的情况 若它的右子树不为空&#…

树状数组(概念 + 模板 + 例题)

1 . 概念 修改 &#xff0c; 就是从前往后修 &#xff0c; 查寻&#xff0c;就是从后往前查&#xff0c;然后累加 ; 2 . 模板 : #define lowbit(x) (x&(-x)) // 获取最后一个1 const int N 5e510;int n , m , s[N] ; // 向后修 void change(int x , int k){while(x&l…

全自动引流,每日500+粉丝的秘诀

在如今竞争激烈的市场环境下&#xff0c;如何有效地吸引和保持精准粉丝成为了每个企业主或网红必须面对的问题。然而&#xff0c;许多人可能误以为全自动引流就意味着无人参与&#xff0c;实际上&#xff0c;它更多的是借助一些自动化工具和策略来提升我们的工作效率。今天&…