C++逆向学习(三) 移动构造函数

最近研究了一下C++类的 移动构造函数 ,同时也进行了一些逆向分析,过程中碰到一个很奇怪的问题,以此记录

相关背景

右值引用

右值引用主要是为了解决 C++98/03 遇到的两个问题

临时对象非必要的昂贵的拷贝操作

模板函数中如何按照参数的实际类型进行转发

本文主要探讨问题1,一些代码尝试和IDA中逆向的分析

学习链接:从4行代码看右值引用,这里就不多说了

move语义

比如在 vector.push_back(str) 时, str(类) 作为实参,会复制一份自身成为形参,进入函数调用

而这个过程中就会产生 临时对象 ,那么也就会调用 拷贝构造函数

而如果 vector.push_back(std::move(str)) ,就可以匹配 移动构造函数 ,省去这个拷贝过程以提高效率

链接中已经解释的很详细了,不再赘述,总之就是 给将亡值续命 ,延长它的生命周期(原本很可能是一个临时变量)

代码分析

接下来的部分内容可以作为上一篇文章C++逆向学习(二) vector的补充,在分析 移动构造函数时又学到了一些之前没有注意过的 vector 的细节

Str类源码

#include#include#includeusingnamespacestd;classStr {public:char* str;    Str(charvalue[]) {cout<<"Ordinary constructor"

代码1

main 函数中,不使用 move 语义,会调用拷贝构造函数

intmain(intargc,char** argv){charvalue[] ="template";Strs(value);vector vs;    vs.push_back(s);return0;}

IDA打开如下

简单的流程,甚至 Str 的高亮都是对称的

最初调用 Str 的拷贝构造函数,匹配的是 Str(char value[]) ,接着初始化 vector ,然后一次 push_back(s)

跟进 push_back

一开始仍然是熟悉的判断 vector 的 size & capacity 的关系,最终调用的是这里的复制构造函数

注意第一个参数是 this ,是C++成员函数调用时的第一个参数,类指针

运行结果:

代码2

代码2,只 move(s)

intmain(intargc,char** argv){charvalue[] ="template";Strs(value);vector vs;//vs.push_back(s);//cout<<"-----------------"<

IDA打开如下:

注意到其中的 std::move ,跟进发现其实实现只有一句话

也印证了 move 实际上不移动任何东西,唯一的功能是 将一个左值强制转换为一个右值引用

继续跟进

仍然是判断大小和容量的代码,接着调用的是 移动构造函数

运行结果:

代码3

这段代码实际上只是在 单纯move 之前加上了一句 push_back(s) ,但是运行结果差了很多

作为对 vector逆向学习 的补充

"我全都要"写法,同时用拷贝构造和移动构造

intmain(intargc,char** argv){charvalue[] ="template";Strs(value);vector vs;    vs.push_back(s);cout<<"-----------------"<

按理来说,输出结果也只应该比代码2多一个 copy constructor 和 destructor ,但实际上多了很多东西

IDA打开并没有出乎意料的结果,仍然是清晰的两次 push_back ,跟进后也没有什么特别的发现,查看交叉引用也没能找到相关信息

为什么在 move 之后还会有一次 copy ,对应的之后又多了一个 desctructor ?

首先, vector 虽然是值语义,但是 move 过后,既然已经调用了 移动构造函数 ,肯定不会再无聊的拷贝一次

在 vs 里调试,输出各个时间点的 capacity

注意 第一个destructor 和 容量2 的出现时间

跟进源码好久后才发现,多的 copy 的产生原因,是因为 vector 内部动态扩容时,在新开辟的空间上调用了 复制构造函数

也就是说把原来的一个 Str s 复制到了新内存空间,这个过程并没有调用 移动构造函数

可能这也是写了移动构造函数后,保险起见也要写一个复制构造函数的原因

其他

考虑这个问题

为什么 vector 内部扩容时,要在新地址调用拷贝构造函数呢?

之前文章已经分析过, vector 实际上只存了类型的数据结构

直接 memcpy(new_memory,old_memory,size) ,再把旧内存空间清零,会造成什么问题?

查了一些资料后发现,扩容是 allocator 的事情,一个可能的实现是 原位new

而如果直接 memcpy ,会不会出问题取决于 vector 存的类型是否平凡(POD)

POD是 Plain old data structure 的缩写

资料提到 shared_ptr 也可能会被影响,取决于引用计数放在哪里

但无论如何,指针的浅拷贝、深拷贝问题值得注意,否则在 vector 内部扩容时,可能2个指针指向同一块内存,析构时会产生严重的错误

看我主页简介免费C++学习资源,视频教程、职业规划、面试详解、学习路线、开发工具

每晚8点直播讲解C++编程技术。

你可能感兴趣的:(C++逆向学习(三) 移动构造函数)