首页 > 编程语言 >用汇编的眼光看C++(之算术符重载)

用汇编的眼光看C++(之算术符重载)

时间:2022-11-23 11:01:13浏览次数:42  
标签:汇编 mov C++ ebp desk 重载 dword ptr



    算术符重载是类的有一个特性,但是每个人使用的方法不一样。用的好,则事半功倍;但是如果不正确的使用,则会后患无穷。

    (1) 简单算术符介绍

    那什么是算术符重载呢?我们可以举个例子。一般来说,我们定义两个int类型的变量的话,我们就可应对这两个类型进行加、减、乘、除的操作,同时还能比较判断、打印、数组操作、*号操作等等。那么如果我们想自己定义的类也具有这样的属性,那我们应该怎么办呢?当然就要算术符重载了。首先,我们对基本class做一个定义:

class desk
{
public:
int price;

desk(int value):price(value) {}
~desk() {}
desk& operator+= (desk& d){
this->price += d.price;
return *this;
}
};

    下面,可以用一个范例函数说明一下使用的方法:

74:       desk n(5);
0040126D push 5
0040126F lea ecx,[ebp-10h]
00401272 call @ILT+0(desk::desk) (00401005)
00401277 mov dword ptr [ebp-4],0
75: desk m(10);
0040127E push 0Ah
00401280 lea ecx,[ebp-14h]
00401283 call @ILT+0(desk::desk) (00401005)
00401288 mov byte ptr [ebp-4],1
76: n += m;
0040128C lea eax,[ebp-14h]
0040128F push eax
00401290 lea ecx,[ebp-10h]
00401293 call @ILT+40(desk::operator+=) (0040102d)
77: }

    大家可以把重点放在76句上面,不过74、75句我们也会稍微介绍一下:

    74句: 创建desk类型的临时变量n,调用构造函数

    75句: 创建desk类型的临时变量m,调用构造函数

    76句: 两个desk类型的数据相加,但是在汇编的形式上面,我们发现编译器把这段代码解释成函数调用,也就是我们在上面定义的算术符重载函数。

     (2)new、free重载

     在C++里面,我们不光可以对普通的算术符进行重载处理,还能对new、free进行重载。通过重载new、free,我们还可以加深对代码的认识,正确认识构造、析构、堆内存分配的原理。

    首先,我们对new和delete进行重载定义:

class desk
{
public:
int price;

desk(int value):price(value) {}
~desk() {}
void* operator new(size_t size) {return malloc(size);}
void operator delete (void* pData) { if(NULL != pData) free(pData);}
};

    那么使用呢?

72:       desk* d =  new desk(10);
0040127D push 4
0040127F call @ILT+65(desk::operator new) (00401046)
00401284 add esp,4
00401287 mov dword ptr [ebp-18h],eax
0040128A mov dword ptr [ebp-4],0
00401291 cmp dword ptr [ebp-18h],0
00401295 je process+56h (004012a6)
00401297 push 0Ah
00401299 mov ecx,dword ptr [ebp-18h]
0040129C call @ILT+5(desk::desk) (0040100a)
004012A1 mov dword ptr [ebp-24h],eax
004012A4 jmp process+5Dh (004012ad)
004012A6 mov dword ptr [ebp-24h],0
004012AD mov eax,dword ptr [ebp-24h]
004012B0 mov dword ptr [ebp-14h],eax
004012B3 mov dword ptr [ebp-4],0FFFFFFFFh
004012BA mov ecx,dword ptr [ebp-14h]
004012BD mov dword ptr [ebp-10h],ecx
73: delete d;
004012C0 mov edx,dword ptr [ebp-10h]
004012C3 mov dword ptr [ebp-20h],edx
004012C6 mov eax,dword ptr [ebp-20h]
004012C9 mov dword ptr [ebp-1Ch],eax
004012CC cmp dword ptr [ebp-1Ch],0
004012D0 je process+91h (004012e1)
004012D2 push 1
004012D4 mov ecx,dword ptr [ebp-1Ch]
004012D7 call @ILT+0(desk::`scalar deleting destructor') (00401005)
004012DC mov dword ptr [ebp-28h],eax
004012DF jmp process+98h (004012e8)
004012E1 mov dword ptr [ebp-28h],0
74: }

    上面是一段普通的new、delete使用代码。但是我们发现,简单的一个语句,在汇编器看来,却需要做这么多的内容,这是为什么呢,我们不妨来自习看一看:

    72句:汇编中有两个函数调用,一个是new调用,也就是我们重定义的new函数,一个是构造函数,最后的几行代码主要是把构造函数返回指针赋值给一些临时变量,可忽略

    73句:汇编中首先让指针和0进行了判断,然后调用了一个函数,似乎没有调用我们的delete函数,我们可以跟进去看一下:

desk::`scalar deleting destructor':
00401410 push ebp
00401411 mov ebp,esp
00401413 sub esp,44h
00401416 push ebx
00401417 push esi
00401418 push edi
00401419 push ecx
0040141A lea edi,[ebp-44h]
0040141D mov ecx,11h
00401422 mov eax,0CCCCCCCCh
00401427 rep stos dword ptr [edi]
00401429 pop ecx
0040142A mov dword ptr [ebp-4],ecx
0040142D mov ecx,dword ptr [ebp-4]
00401430 call @ILT+75(desk::~desk) (00401050)
00401435 mov eax,dword ptr [ebp+8]
00401438 and eax,1
0040143B test eax,eax
0040143D je desk::`scalar deleting destructor'+3Bh (0040144b)
0040143F mov ecx,dword ptr [ebp-4]
00401442 push ecx
00401443 call @ILT+80(desk::operator delete) (00401055)
00401448 add esp,4
0040144B mov eax,dword ptr [ebp-4]
0040144E pop edi
0040144F pop esi
00401450 pop ebx
00401451 add esp,44h
00401454 cmp ebp,esp
00401456 call __chkesp (00408810)
0040145B mov esp,ebp
0040145D pop ebp
0040145E ret 4

    上面的代码便是跟到0x401005之后遇到的代码,这里有一个跳转,真正函数开始的地方是0x401410。这里我们发现函数实际上还是调用了我们定义的delete函数和desk的析构函数。只不过析构函数一定要放在delete调用之前。所以,这里我们就看到了,c++中new的真正含义就是先分配内存,然后调用构造函数;而delete则是先对变量进行析构处理,然后free内存,这就是new和delete的全部意义。掌握了这个基础,可以帮助我们本地对内存进行很好的管理。

    (3)friend算术符重载和普通算术符重载的区别

    有一种算术符的重载是这样的:

class desk
{
int price;
public:
desk(int value):price(value) {}
~desk() {}
friend desk operator+ (desk& d1, desk& d2);
};

desk operator +(desk& d1, desk& d2)
{
desk d(0);
d.price = d1.price + d2.price;
return d;
}

void process()
{
desk d1(3);
desk d2(4);
desk d = d1 + d2;
return;
}

    感兴趣的同学可以汇编看一下,找一找它和普通的非友元函数有哪些区别。不过上面的代码还是让我们看出了一些端倪:

    a)友元函数不属于类,因为定义的时候我们发现没有desk::这样的前缀

    b)友元算术符重载需要比普通的算术符重载多一个输入参数

    c)友元函数在进行算术重载定义的时候需要多定义一个临时变量d,这在函数operator+()可以看出来

    d)友元算术重载函数会破坏原来类地封装性

    e)友元函数实际上就是全局函数


算术运算符使用的经验总结:

    (1)算术重载函数是一把双刃剑,务必小心使用

    (2)内部算术符函数优先使用于非友元函数

    (3)遇到 = 号重载特别注意一下指针

    (4)重载的时候函数的内容要和重载的运算符一致,不用重载的是+,实际运算的是相减的内容

    (5)除非特别需要重载,负责别重载

    (6)重载的时候多复用已经存在的重载运算符

    (7)new、delete除了内存管理和测试,一般不重载,全局new、delete严谨重载

    (8)相关运算符重载要在stl中使用,务必注意返回值



标签:汇编,mov,C++,ebp,desk,重载,dword,ptr
From: https://blog.51cto.com/u_15888909/5880574

相关文章

  • 用汇编的眼光看C++(之虚函数)
       虚函数是面向对象设计中的一个重要内容。它的出现使得我们只需要相同的接口函数,并可以得到不同的生成结果。但是有些朋友却知其然,不知其所以然,为什么会出现这样的结......
  • 用汇编的眼光看C++(之类继承)
       继承是类的一个基本属性,可是在类的继承过程中,函数是怎么初始化?怎么析构的呢?我们不妨看看下面这样的一段代码?classemployee{public:employee(){printf("employee......
  • C/C++模拟校园卡
    C/C++模拟校园卡模拟校园卡出题人:俞琼面向专业:物联网工程难度等级:41问题描述同学们都在机房做实验或自由上机,请根据自己实际使用情况编写一份模拟校园卡消费记录查......
  • C++学校图书馆管理系统
    C++学校图书馆管理系统一、实验目的1.掌握结构体类型的声明方法;2.掌握结构体变量、结构体数组、结构体指针的定义和初始化的方法;3.掌握向函数传递结构体变量、结构体数......
  • c++ 调用 python 2.7
    本来想调用python3.10的,但一直安装不成功,先记录一下python2.7。先上代码:#include<iostream>usingnamespacestd;#include"Python.h"intmain(intargc,cha......
  • P1644 跳马问题 C++ 搜索回溯+dfs
    题目背景在爱与愁的故事第一弹第三章出来前先练练四道基本的回溯/搜索题吧……题目描述中国象棋半张棋盘如图1所示。马自左下角(0,0)向右上角(m,n)跳。规定只能往......
  • P2819 图的m着色问题 C++ 详细题解
    题目背景给定无向连通图G和m种不同的颜色。用这些颜色为图G的各顶点着色,每个顶点着一种颜色。如果有一种着色法使G中每条边的2个顶点着不同颜色,则称这个图是m可着色的。图......
  • [C++]二进制求子集
    这几天做[leetcode1178]猜字谜的题目,用到了很多小的知识技巧点。其中一个就是二进制状态压缩以及如何根据一个二进制序列求其子集,如二进制序列110111001子集里有000000001......
  • Dev-C++5.11安装教程
    (目录)一、下载1.介绍原公司Bloodshed已经停止开发Dev-C++了,但是Embarcadero开发着一个Dev-C++的分支。详见:https://www.embarcadero.com/cn/free-tools/dev-c......
  • 如何利用C++使Windows蓝屏
    如何利用C++使Windows蓝屏虽说windows非常强大,但是使它蓝屏也非常简单:如果你想让Windows蓝屏,你一定会在运行框里输入:cmd/cfor/f%Iin('wmicprocessgetName')d......