C++逆向分析——对象拷贝

对象拷贝

我们通常存储对象,都用数组、列表之类的来存储,那如下所示我们使用数组来存储对象,但是在工作中发现这个数组不够用了,就需要一个更大的数据,但我们重新创建一个数组还需要把原来的数据复制过来;在C语言中可以使用函数来进行拷贝,直接拷贝内存,在C++中实际上跟C语言要做的事情是一样的,在C++中就称之为对象拷贝

images/download/attachments/12714553/image2021-4-17_18-50-20.png

拷贝构造函数

如何在C++中拷贝构造函数,来看一下如下代码:

images/download/attachments/12714553/image2021-4-17_19-1-10.png

可以看见我们定义了一个类,然后创建了两个对象obj、objNew,但是第二个对象的语法很奇怪,传入的参数是第一对象,其实这就是默认拷贝析构函数。

本质就是很简单的内存复制:

images/download/attachments/12714553/image2021-4-17_20-4-58.png

 

在vs2022里实验下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
struct Student {
    int a;
    int b;
 
    Student() {
        printf("Look.");
    }
 
    Student(int a, int b) {
        this->a = a;
        this->b = b;
    }
 
    ~Student() {   
        printf("Look A.");
    }
 
};
 
void main() {
    Student s(1, 2);
    Student s2(s);
    return;
}

 反汇编码可以看到,的确就是无脑式的内存copy!

1
2
3
4
5
6
7
8
9
10
11
    Student s(1, 2);
00FE458F 6A 02                push        2 
00FE4591 6A 01                push        1 
00FE4593 8D 4D F0             lea         ecx,[s] 
00FE4596 E8 2A CE FF FF       call        Student::Student (0FE13C5h) 
    Student s2(s);
00FE459B 8B 45 F0             mov         eax,dword ptr [s] 
00FE459E 8B 4D F4             mov         ecx,dword ptr [ebp-0Ch] 
00FE45A1 89 45 E0             mov         dword ptr [s2],eax 
00FE45A4 89 4D E4             mov         dword ptr [ebp-1Ch],ecx 
    return;

在odb里看到的内存copy示例(我觉得奇怪的就是为啥不是连续4个地址变量呢?):

 

 

 

上面的内存复制是在栈中,而我们想在堆中去拷贝可以这样写:

CObject* p = new CObject(obj);

如上所示,我们是通过拷贝析构函数在内存中创建了一个新的对象,而如果该类本身有一个父类,父类会被拷贝吗?我们写一段代码来论证一下:

images/download/attachments/12714553/image2021-4-17_22-28-37.png

那么这段代码,拷贝构造函数不仅可以将当前对象的内容复制,还可以将父类的内容复制过来。

拷贝构造函数是编译器已经提供的,其已经非常成熟了,通常情况下是不建议自己写拷贝构造函数的,除非出现类中包含指针类型的成员,这种情况是需要自己重些拷贝构造函数的,因为拷贝构造函数只是会拷贝当前指针成员的值,并不会拷贝指针成员指向的内容。

所以这种拷贝方式,我们可以称之为浅拷贝;而如果可以做到能够复制成员的情况下,还可以将指针指向的内存地址复制过来,并自动申请一块新的内存提供,这种方式我们称之为深拷贝

如果要实现深拷贝,我们就需要自己重写拷贝构造函数。

MyObject(const MyObject& obj) {
m_nLength = obj.m_nLength;
m_strBuffer = new char[m_nLength];
memset(m_strBuffer, 0, m_nLength);
strcpy(m_strBuffer, obj.m_strBuffer);
}

images/download/attachments/12714553/image2021-4-17_23-48-7.png

需要注意的是这个格式是固定的,不要自己「自由发挥」。

总结:

  1. 如果不需要深拷贝,就不要自己添加拷贝构造函数;

  2. 如果你天假了拷贝构造函数,那么编译器将不再提供,所有的事情都需要由新添加的函数自己来处理。

重载赋值运算符

上一章学习了如何通过拷贝对象函数的方式来实现对象拷贝,这里就来学习使用重载赋值运算符实现对象拷贝。

在C++中是允许我们在两个对象之间直接使用赋值运算符的:

CObject a(1, 2), b(3, 4);
b = a;

images/download/attachments/12714553/image2021-4-18_16-26-21.png

那么赋值运算符实现的对象复制是否会当前复制对象继承的父类进行复制呢?

images/download/attachments/12714553/image2021-4-18_16-36-55.png

如上图所示,赋值运算符是可以复制父类的。

但是在这里赋值运算符是否就非常完美了呢?不是的,赋值运算符和拷贝构造函数是有相同缺点的,那就是其默认都是浅拷贝

我们想要解决这个问题就需要重写一个赋值运算符,自己来实现深拷贝

images/download/attachments/12714553/image2021-4-18_21-45-51.png

posted @   bonelee  阅读(132)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
历史上的今天:
2020-04-09 OWASP TOP10 Web应用十大安全风险_2017
2020-04-09 移动互联网恶意程序描述格式 ——移动端恶意软件分类再合适不过了
2020-04-09 用短信传播病毒:最新Android手机勒索软件Koler
2020-04-09 2019年Android恶意软件专题报告:未来移动安全呈现四大趋势——资费消耗与隐私窃取分别以高达46.8%和41.9%的占比,成为横行无忌的主要恶意软件类型,其次分别为远程控制、流氓行为、恶意扣费和欺诈软件。
2020-04-09 2018年Android恶意软件专题报告
2020-04-09 浅析Android恶意应用及其检测技术
2020-04-09 Android恶意软件特征及分类
点击右上角即可分享
微信分享提示