2023最新C++面经(一):vector内存预分配,左值引用和右值引用,move语义
文章目录
- 零、前言
- 一、在C++中,往vector插入1000个数字,怎么做能保证性能最高
- 二、在vector中对10000个数字删除偶数位置的数,怎么做保证性能较高
- 三、malloc用delete会出现什么问题
- 四、weak_ptr解决的是什么问题,lock返回的对象可以直接使用吗
- 五、C++中的左值引用和右值引用是什么? 如果不写移动构造函数,那调用的会是什么?
- 六、移动构造函数实例
- 七、std::move
- 八、对于map和unordered_map容器怎么分别做到对字母的大小写不敏感
零、前言
今天在朋友圈看到别人发的一套C++面经,特意思考了一下
一、在C++中,往vector插入1000个数字,怎么做能保证性能最高
以下是一些可以提高在C++中向vector中插入大量数字性能的技巧:
- 预先分配内存空间:使用vector::reserve函数预先分配足够的内存空间,以避免在插入元素时进行重新分配内存的开销。例如,如果你需要插入1000个数字,可以使用vector::reserve(1000)在插入前先为向量分配1000个元素的空间。
- 使用移动语义:使用std::move将元素插入vector,而不是使用拷贝构造函数。因为拷贝构造函数会导致新的内存分配和数据复制,而使用移动语义可以避免这些开销。例如,可以使用以下代码向vector中插入一个元素:
vector<int> v;
int x = 123;
v.push_back(std::move(x));
- 批量插入元素: 使用std::vector::insert函数批量插入元素,而不是使用单个插入函数。因为单个插入函数可能会多次进行内存分配和内存复制,而批量插入函数可以减少这些开销:
vector<int> v;
vector<int> data(1000);
v.insert(v.end(),data.begin(),data.end());
- 使用移动迭代器: 使用std::make_move_iterator将元素转换为移动迭代器以避免拷贝构造函数的开销。
std::vector<std::string> v;
std::string str = "hello";
v.insert(v.end(), std::make_move_iterator(std::begin(str)), std::make_move_iterator(std::end(str)));
二、在vector中对10000个数字删除偶数位置的数,怎么做保证性能较高
以下是一些可以提高在C++中从vector中删除偶数位置的数字的性能的技巧:
- 使用erase-remove惯用法:使用erase-remove惯用法从vector中删除偶数位置的数字。该惯用法的思想是先使用std::remove_if算法将所有需要删除的元素移动到vector的末尾,然后再使用std::vector::erase函数删除这些元素。例如,可以使用以下代码从vector中删除偶数位置的数字:
std::vector<int> v{...}; // 假设有10000个数字
v.erase(std::remove_if(v.begin(), v.end(), [](const auto& x){ return &x - &v[0] % 2 == 0; }), v.end());
- 使用reserve预分配内存:在删除元素之前使用std::vector::reserve函数预分配足够的内存,以避免在移动元素时进行重新分配内存的开销。例如,可以使用以下代码预分配内存:
std::vector<int> v{...}; // 假设有10000个数字
v.reserve(v.size() / 2);
- 使用迭代器递增器:在使用std::remove_if算法时,使用迭代器递增器std::next来遍历vector中的元素,而不是使用索引操作符。例如,可以使用以下代码遍历vector中的元素
std::vector<int> v{...}; // 假设有10000个数字
v.erase(std::remove_if(v.begin(), v.end(), [i = 0](const auto&){ return i++ % 2 == 0; }), v.end());
该代码使用了lambda表达式作为std::remove_if算法的谓词,其中[i = 0]表示定义了一个名为i的变量,并初始化为0。i++ % 2 == 0表示先取i的值,然后将i加1,再判断该值是否为偶数。使用迭代器递增器可以避免使用索引操作符的性能开销。
在这句代码中,const auto&是一个lambda表达式的参数,它代表了一个引用类型的元素。lambda表达式是一个匿名函数,它可以接受一个或多个参数,这些参数可以是值类型、引用类型或者指针类型。
在这个lambda表达式中,使用const auto&作为参数,表示接受一个任意类型的常量引用。在std::remove_if算法中,该lambda表达式作为谓词被调用,每次调用时会传入vector中的一个元素,并对该元素进行判断。由于lambda表达式中的参数是一个常量引用,所以在对该元素进行判断时不会改变原始vector中的元素。
在该lambda表达式中,i++ % 2 == 0表示先取i的值,然后将i加1,再判断该值是否为偶数。由于在lambda表达式中定义了一个变量i,所以可以通过该变量来判断元素在vector中的位置是否为偶数位置。
三、malloc用delete会出现什么问题
malloc()和delete是两个不同的内存分配和释放函数,它们具有不同的行为和约定。使用malloc()分配的内存需要使用free()函数进行释放,而使用new运算符分配的内存需要使用delete运算符进行释放。因此,如果使用delete释放使用malloc()分配的内存,会导致内存错误和未定义的行为。
使用delete释放使用malloc()分配的内存,可能会导致以下问题:
-
内存泄漏:如果使用delete释放使用malloc()分配的内存,将无法正确释放内存,导致内存泄漏。
-
崩溃或异常:使用delete释放使用malloc()分配的内存,可能会导致程序崩溃或抛出异常,因为delete假设它的参数是通过new分配的内存。
-
不正确的内存释放:使用delete释放使用malloc()分配的内存,可能会释放掉不属于该内存块的内存,或者释放掉一个已经被释放的内存块,导致不正确的内存释放。
四、weak_ptr解决的是什么问题,lock返回的对象可以直接使用吗
weak_ptr是C++11中引入的一种智能指针类型,用于解决shared_ptr循环引用导致的内存泄漏问题。在使用shared_ptr进行循环引用时,如果存在两个或多个shared_ptr相互引用,那么它们之间的引用计数永远不会达到0,导致分配的内存永远无法被释放。而weak_ptr则可以作为一个非拥有者指针,指向shared_ptr所管理的对象,但不会增加引用计数,因此不会导致循环引用和内存泄漏问题。
lock()函数是weak_ptr提供的一个成员函数,用于返回一个指向weak_ptr所管理对象的shared_ptr对象。lock()函数首先检查weak_ptr所指向的对象是否还存在,如果存在,则返回一个指向该对象的shared_ptr对象;否则,返回一个空的shared_ptr对象。使用lock()函数获得shared_ptr对象后,可以使用该对象来访问weak_ptr所管理的对象。需要注意的是,在使用lock()函数获得shared_ptr对象之前,需要检查lock()返回的shared_ptr对象是否为空,如果为空则表示weak_ptr所指向的对象已经被销毁了。
返回的shared_ptr对象可以直接使用,但需要注意其生命周期。shared_ptr对象在所有指向该对象的shared_ptr对象都被销毁后才会自动销毁,因此如果需要使用shared_ptr对象,需要确保该对象的生命周期符合预期,以避免内存泄漏等问题。另外,由于weak_ptr对象只是一个非拥有者指针,它并不拥有shared_ptr所管理的对象,因此需要确保在使用lock()函数返回的shared_ptr对象之前,shared_ptr所管理的对象仍然存在,否则会出现未定义的行为。
五、C++中的左值引用和右值引用是什么? 如果不写移动构造函数,那调用的会是什么?
左值引用和右值引用都是C++中的引用类型,用于表示一个对象的别名。其中左值引用表示对一个左值对象的引用,而右值引用表示对一个右值对象(包括临时对象和将要销毁的对象)的引用。
左值引用的声明形式为T&,表示对类型为T的左值对象的引用;而右值引用的声明形式为T&&,表示对类型为T的右值对象的引用。例如,以下代码声明了一个左值引用和一个右值引用:
int x = 123;
int& lref = x; // 左值引用
int&& rref = 123; // 右值引用
在C++11之前,如果没有编写移动构造函数,当一个对象被移动构造时,实际上是调用了该对象的拷贝构造函数。因此,如果一个类没有显式定义移动构造函数,但是有拷贝构造函数,当使用右值引用移动构造该对象时,实际上是调用该类的拷贝构造函数。
在C++11及之后的版本中,如果一个类定义了移动构造函数,那么当使用右值引用移动构造该对象时,实际上是调用该类的移动构造函数。移动构造函数是一种特殊的构造函数,用于将一个对象的资源(如堆内存、文件句柄等)从一个对象转移到另一个对象,而不进行数据的复制。使用移动构造函数可以避免拷贝大量数据的开销,提高程序的性能。
需要注意的是,如果一个类既定义了拷贝构造函数,又定义了移动构造函数,那么在使用右值引用移动构造该对象时,会优先调用移动构造函数。如果没有定义移动构造函数,但是定义了移动赋值运算符,那么在使用右值引用进行赋值操作时,会调用移动赋值运算符。如果同时没有定义移动构造函数和移动赋值运算符,但是定义了析构函数,那么在对象销毁时,会使用拷贝构造函数进行拷贝构造。如果以上所有函数都没有定义,那么编译器会生成默认的函数实现。
六、移动构造函数实例
class MyClass {
public:// 移动构造函数MyClass(MyClass&& other) noexcept {// 将other对象的资源移动到当前对象data_ = other.data_;size_ = other.size_;// 将other对象的资源释放other.data_ = nullptr;other.size_ = 0;}// 其他成员函数// ...private:int* data_;size_t size_;
};
该移动构造函数使用右值引用作为参数类型,并且使用了noexcept关键字,表示该函数不会抛出异常。在移动构造函数中,首先将另一个对象的资源移动到当前对象,然后将另一个对象的资源释放,以避免资源的重复释放。
在该例子中,假设data_是一个指向堆内存的指针,size_表示堆内存的大小。在移动构造函数中,将另一个对象的data_指针和size_成员移动到当前对象,然后将另一个对象的data_指针置为nullptr,size_成员置为0,以表示另一个对象的资源已经被移动到当前对象。
移动构造函数可以在类中定义,也可以在类外定义。如果在类外定义,需要在函数名前添加类名和作用域解析符::。需要注意的是,移动构造函数的参数应该是一个非const右值引用,并且应该使用noexcept关键字标记,以便在移动操作时可以获得更好的性能和安全性。
移动构造函数的参数为什么要是一个非const右值引用呢? 这是因为移动构造函数的参数为非const右值引用是为了表示该参数是一个将要销毁的对象(右值对象),并且该对象的资源可以被移动到另一个对象中。非const右值引用参数的特点是不能被赋值、取地址和修改值,因此移动构造函数可以对该参数进行资源的“窃取”,而不必担心该对象被修改或复制。此外,由于移动构造函数可以修改参数对象的状态,因此该参数不能是const类型的,否则会导致编译错误。
七、std::move
std::move是C++11引入的一个函数模板,用于将一个左值转换为右值引用,以便在移动构造函数和移动赋值运算符中使用。std::move本质上只是将一个左值强制转换为右值引用,它并不会实际地移动对象或改变对象的状态。
在正常情况下,使用std::move可以将一个左值对象的资源(如堆内存、文件句柄等)移动到一个新的对象中,从而避免了拷贝大量数据的开销,提高了程序的性能。使用std::move时需要注意以下几点:
- std::move只能用于可移动的对象:使用std::move时,需要确保对象的类型支持移动语义,即有移动构造函数和移动赋值运算符。如果对象没有移动构造函数和移动赋值运算符,则无法使用std::move进行移动操作。
- 移动后对象的状态:使用std::move将一个对象的资源移动到另一个对象中后,原始对象的状态可能会发生变化,例如指针会被置为空、计数器会被减少等。需要确保移动后对象的状态符合预期,以避免错误的操作。
- 临时对象的生命周期:在某些情况下,使用std::move将一个左值对象转换为右值引用后,会生成一个临时对象。例如,将一个std::vector对象传递给函数时,如果使用std::move将该对象转换为右值引用,则会生成一个临时对象。需要确保临时对象的生命周期符合预期,以避免临时对象在不适当的时候被销毁或者访问已经失效的对象。
八、对于map和unordered_map容器怎么分别做到对字母的大小写不敏感
在map和unordered_map容器中实现大小写不敏感,可以通过自定义比较函数来实现。具体来说,可以重载std::map和std::unordered_map容器中的Compare类型,定义一个自己的比较函数对象,并将其作为容器的第三个模板参数传递进去。比较函数对象需要重载operator()运算符,对于字符串类型的键,可以在比较之前将所有字符转换成小写或大写字母,然后再进行比较。
以下是一个示例代码:
#include <iostream>
#include <map>
#include <unordered_map>
#include <string>
#include <algorithm>struct CaseInsensitiveLess
{bool operator()(const std::string& lhs, const std::string& rhs) const{std::string lstr(lhs);std::string rstr(rhs);std::transform(lstr.begin(), lstr.end(), lstr.begin(), ::tolower);//transform函数: transform是一个通用算法,用于对指定区间内的元素进行转换,并将结果存储在另一个区间中。transform算法接受两个输入迭代器和一个输出迭代器作为参数,以及一个一元或二元操作函数对象(可通过函数指针、函数对象、Lambda表达式等形式指定),将指定区间内的元素依次传入操作函数进行转换,然后将转换后的结果存储到输出迭代器指向的位置。std::transform(rstr.begin(), rstr.end(), rstr.begin(), ::tolower);return lstr < rstr;}
};int main()
{std::map<std::string, int, CaseInsensitiveLess> m;m["a"] = 1;m["B"] = 2;m["c"] = 3;std::cout << m["A"] << std::endl; // 输出 1std::cout << m["b"] << std::endl; // 输出 2std::unordered_map<std::string, int, std::hash<std::string>, CaseInsensitiveLess> um;um["a"] = 1;um["B"] = 2;um["c"] = 3;std::cout << um["A"] << std::endl; // 输出 1std::cout << um["b"] << std::endl; // 输出 2return 0;
}
相关文章:
2023最新C++面经(一):vector内存预分配,左值引用和右值引用,move语义
文章目录零、前言一、在C中,往vector插入1000个数字,怎么做能保证性能最高二、在vector中对10000个数字删除偶数位置的数,怎么做保证性能较高三、malloc用delete会出现什么问题四、weak_ptr解决的是什么问题,lock返回的对象可以直接使用吗五、…...
【C语言经典例题】调整数组使奇数全部都位于偶数前面
目录 一、题目要求 二、解题思路 分步解析 从前往后找 从后往前找 交换 三、完整代码演示 一、题目要求 输入一个整数数组,实现一个函数, 来调整该数组中数字的顺序使得数组中所有的奇数位于数组的前半部分, 所有偶数位于数组的后半…...
C++经典20题型,满满知识,看这一篇就够了(含答案)
今天找了20道c的经典题型,看这一篇就够了,全是干货 目录 1、题目:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总…...
卷积神经网络CNN之ZF Net网络模型详解(理论篇)
1.背景 2. ZF Net模型结构 3. 改进优缺点 一、背景 ZF Net是用作者的名字命名的,Matthew D.Zeiler 和 Rob Fergus (纽约大学),2013年撰写的论文; 论文原网址https://arxiv.org/abs/1311.2901 论文名:Vis…...
Vue 3.0 响应性 基础 【Vue3 从零开始】
#声明响应式状态 要为 JavaScript 对象创建响应式状态,可以使用 reactive 方法: import { reactive } from vue// 响应式状态const state reactive({count: 0}) reactive 相当于 Vue 2.x 中的 Vue.observable() API ,为避免与 RxJS 中的 ob…...
flex布局方式让最后一个(或第二个...n)元素居右显示
<div class"round"> <div class"income">收入</div> <div class"center"> <img style"width: 12px" src"../../img/big/up.png"> </div> <div class"rg"> <span cl…...
【Python语言基础】——Python MySQL Order By
Python语言基础——Python MySQL Order By 文章目录 Python语言基础——Python MySQL Order By一、Python MySQL Order By一、Python MySQL Order By 结果排序 请使用 ORDER BY 语句按升序或降序对结果进行排序。 ORDER BY 关键字默认按升序对结果进行排序。若要按降序对结果进…...
自然数学的哲学原理--复数理论的扩展
自然数学的哲学原理--复数理论的扩展 2023-03-05 10:27:12 自然数学的哲学原理--复数理论的扩展 一维:线,实数 二维:平面 三维:立体 四维:相对论时空 复数,以一个数对形式表示,实现了复平面的…...
tsconfig.json中的一些配置
compilerOptions 编译选项是配置文件中非常重要也比较复杂的配置选项 target:设置ts代码编译的目标版本 可选值: ES3(默认)、ES5、ES6/ES2015、ES7/ES2016、ES2017、ES2018、ES2019、ES2020、 ESNext 示例: &quo…...
Spark调优总结
下面是基于官方优化建议,加上自己的一些理解整理。官方地址:https://spark.apache.org/docs/2.4.8/tuning.html 任务并行度 Spark会根据每个文件的大小自动设置运行“map”任务的数量,而对于分布式的“reduce”操作,例如groupBy…...
4.创建和加入通道相关(network.sh脚本createChannel函数分析)[fabric2.2]
fabric的test-network例子有一个orderer组织、两个peer组织、每个组织一个节点,只有系统通道(system-channel),没有其他应用通道。我们可以使用./network.sh createChannel命令来创建一个名为mychannel的应用通道。 一、主要概念 …...
若依学习(前后端分离版)——自定义注解@Log(如何自定义注解,实现aop)
如何自定义注解 aop的基本知识与应用 若依对用户的一些更新删除等敏感操作操作进行了日志记录 注解定义和切面处理的项目位置 第一步:自定义注解log 定义了注解的相关信息。这里定义的属性可以在使用时加以定义 注解Target和Retention的作用 第二步切面逻辑…...
防止暴力破解ssh的四种方法
一. 方法介绍 防止暴力破解的四种方法: 1 密码要写的足够的复杂,通常建议将密码写16位,并且无连贯的数字或者字母;当然也可以固定一个时间修改一次密码,推荐是一个月修改一次会稳妥一些2 修改ssh的端口号,…...
jsp试卷分析管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目
一、源码特点 JSP试卷分析管理系统是一套完善的java web信息管理系统,对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发,数据库为Mysql5.0&…...
可选链运算符(?.)与空值合并运算符(??)
1. 可选链运算符Optional chaining(?.) MDN定义 可选链运算符(?.)允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。?. 运算符的功能类似于 . 链式运算符,不同之处在于,在引用为空 (n…...
JavaScript 闭包
JavaScript 变量可以是局部变量或全局变量。私有变量可以用到闭包。全局变量函数可以访问函数内部定义的变量,如:实例function myFunction() {var a 4;return a * a;}尝试一下 函数也可以访问函数外部定义的变量,如:实例var a 4…...
每日记录自己的Android项目(二)—Viewbinding,WebView,Navigation
今日想法今天是想把做一个跳转页面的时候调到H5页面去,但是这个页面我用app来承载,不要调到浏览器去。所以用到了下方三个东西。Viewbindingbuild.gradle配置首先在app模块的build.gradle里添加一下代码默认情况下,每一个布局xml文件都会生成…...
20230305英语学习
Climate Change Is Suffocating Large Parts of the Ocean 研究:气候变化正在使海洋“缺氧” One day more than a decade ago, Eric Prince was studying the tracks of tagged fish when he noticed something odd.Blue marlin off the southeastern United State…...
【Linux】手把手教你在CentOS上使用docker 安装MySQL8.0
文章目录前言一. docker的安装1.1 从阿里下载repo镜像1.2 安装docker1.3 启动docker并查看版本二. 使用docker安装MySQL8.02.1 拉取MySQL镜像2.2 创建容器2.3 操作MySQL容器2.4 远程登录测试总结前言 大家好,又见面了,我是沐风晓月,本文主要…...
论文解读:High Dynamic Range and Super-Resolution from Raw Image Bursts
论文解读:High Dynamic Range and Super-Resolution from Raw Image Bursts 今天介绍一篇发表于 2022 年 ACM Tranaction on Graphic 上的文章,这篇文章通过多帧曝光将 HDR 与 SR 放在一起解决,与一般的文章不同的地方在于,这篇文…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...
智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
WPF八大法则:告别模态窗口卡顿
⚙️ 核心问题:阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程,导致后续逻辑无法执行: var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题:…...
redis和redission的区别
Redis 和 Redisson 是两个密切相关但又本质不同的技术,它们扮演着完全不同的角色: Redis: 内存数据库/数据结构存储 本质: 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能: 提供丰…...
xmind转换为markdown
文章目录 解锁思维导图新姿势:将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件(ZIP处理)2.解析JSON数据结构3:递归转换树形结构4:Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...
