C++愤恨者札记1——类对象作为函数参数的数据传递过程
C++繁杂的机制,加上枯燥的教科书,再加上无法回避地要使用它,注定要造就一批C++愤恨者。本文作为C++愤恨者札记系列第一篇,从汇编角度,观察类对象作为函数参数时的数据传递过程。
若没有特殊说明,编译器使用的是VC++,反汇编使用的是Windbg.下面是它们的版本号:
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86
Microsoft (R) Windows Debugger Version 6.11.0001.404 X86
测试代码如下:
class Node
{
public:
Node(){}
//Node(Node& n);
int data1;
int data2;
int data3;
int data4;
int data5;
int data6;
int data7;
};
//Node::Node(Node &n)
//{
//}
void Fn( int a, Node n, int b )
{
n.data1 = 100;
n.data2 = 100;
a = 100;
b = 10;
}
void main()
{
Node n;
Fn(1, n, 2);
}
--------------------------------------------------
未使用拷贝构造函数时,调用Fn的反汇编代码:
00fa1421 6a02 push 2 ;第三个参数入栈
00fa1423 83ec1c sub esp,1Ch ;为Node n分配栈内存, 注意,构造函数Node(),并没调用
00fa1426 b907000000 mov ecx,7 ;rep循环次数
00fa142b 8d75e0 lea esi,[ebp-20h] ;Node n地址
00fa142e 8bfc mov edi,esp ;栈空间地址
00fa1430 f3a5 rep movs dword ptr es:[edi],dword ptr [esi] ;把n内容拷贝到栈空间上
;A5 MOVS m32, m32 Move doubleword
;at address DS:(E)SI to address ES:(E)DI
00fa1432 6a01 push 1 ;第一个参数入栈
00fa1434 e8a2fdffff call hello!ILT+470(?FnYAXHVNodeHZ) (00fa11db)
00fa1439 83c424 add esp,24h ;恢复栈平衡,4+1CH+4=24H
类对象参数位于栈上,是通过sub esp size来分配的。数据是通过内存拷贝来初始化。
--------------------------------------------------
使用拷贝构造函数时,即上面代码把注释去掉,调用Fn的反汇编代码:
01002406 6a02 push 2 ;第三个参数入栈
01002408 83ec1c sub esp,1Ch ;开辟栈空间
0100240b 8bcc mov ecx,esp ;栈内存首址保存在ecx中,拷贝构造函数的this指针
0100240d 8d45e0 lea eax,[ebp-20h] ;实参地址
01002410 50 push eax ;作为拷贝构造函数的参数
01002411 e8d4edffff call hello!ILT+485(??0NodeQAEAAV0Z) (010011ea) ;拷贝构造函数,替换了rep movs内存拷贝
01002416 6a01 push 1 ;第一个参数入栈
01002418 e8beedffff call hello!ILT+470(?FnYAXHVNodeHZ) (010011db)
0100241d 83c424 add esp,24h ;恢复栈平衡
类参数仍然位于栈上,也是通过sub esp size来分配的。数据是通过拷贝构造函数初始化的,C++的机制就是繁多--||。
--------------------------------------------------
下面是Fn的反汇编结果,它可不管Node n是怎么初始化的,只要把它在栈上的位置找到就OK啦。
hello!Fn:
00a41a60 55 push ebp | old ebp | ebp
00a41a61 8bec mov ebp,esp |-------------|
| ret address | ebp+4
00a41a63 81ecc0000000 sub esp,0C0h |-------------|
| int a | ebp+8
00a41a69 53 push ebx |-------------|
00a41a6a 56 push esi | Node n | ebp+0CH
00a41a6b 57 push edi |-------------|
| int b | ebp+28H
00a41a6c 8dbd40ffffff lea edi,[ebp-0C0h]
00a41a72 b930000000 mov ecx,30h
00a41a77 b8cccccccc mov eax,0CCCCCCCCh
00a41a7c f3ab rep stos dword ptr es:[edi] ;以是为局部变量空间初始化,debug版特有的
00a41a7e c7450c64000000 mov dword ptr [ebp+0Ch],64h ;n.data1 = 100; 显示ebp+0Ch是参数n的起始地址
00a41a85 c7451064000000 mov dword ptr [ebp+10h],64h ;n.data2 = 100;
00a41a8c c7450864000000 mov dword ptr [ebp+8],64h ;a = 100;
00a41a93 c745280a000000 mov dword ptr [ebp+28h],0Ah ;b = 10;
00a41a9a 5f pop edi
00a41a9b 5e pop esi
00a41a9c 5b pop ebx
00a41a9d 8be5 mov esp,ebp
00a41a9f 5d pop ebp
00a41aa0 c3 ret
--------------------------------------------------
总结:
类对象做为函数参数时,是被存放在栈上的,不影响实参的数据。
若未重写拷贝构造函数,类的其它构造函数将不会被调用。形参的数据是通过内存拷贝传递的。若重写了,拷贝构造函数将会在初始化形参时被调用,不再进行内存拷贝工作。
补充1:
如果你手贱,添加了一个空的拷贝构造函数,那么默认的拷贝构造函数,即那个rep movs拷贝内存的,将会被抹死掉,你的参数将得不到初始化。下面的代码将得不到想要的结果。
#include <iostream>
using namespace std;
class Node
{
public:
Node(){}
Node(Node& n)
{
/*啥都不做*/
}
int data;
};
void ShowNodeData( Node n)
{
cout << n.data << endl;
}
void main()
{
Node n;
n.data = 100;
ShowNodeData(n); //参数得不到初始化
}
分享到:
相关推荐
对象作为函数参数 对象本身做参数(传值),传对象副本 对象引用做参数(传地址),传对象本身
C++面向对象程序设计——基础、数据结构与编程思想 (第4版)
在C++中,通过多维数据的指针作为函数参数传递源程序
dlut可视化大作业3————复变函数积分可视化
数据结构的函数参数传递,如何在C++中使用参数传递
有默认参数的函数 函数调用时形参从实参那里取值,so实参与形参一致,有时多次调用同一函数用同样的实参,可以直接给形参一个默认的... 函数不能既作为重载函数,又作为有默认参数的函数,会造成系统无法判定,出错。
这是函数参数传递方式,在学c++时可能会需要,希望对大家有所帮助
其错误是普通的C++成员函数都隐含了一个传递函数作为参数,亦即“this”指针,C++通过传递this指针给其成员函数从而实现程序函数可以访问C++的数据成员。这也可以理解为什么C++类的多个实例可以共享成员函数却-有...
数据结构与算法——面向对象的C++设计模式
c# 调用C++编写 的DLL函数各种参数传递问题。数据处理问题等等。
C_C++语言硬件程序设计——基于TMS320C5000系列DSP
c++之指针作为函数参数传递的问题的pdf版本 博客:http://blog.csdn.net/fjb2080 欢迎访问!
但是当要进行查找、删除、修改操作时,系统只能对第一个对象进行操作 查看程序时,发现查找函数、删除和修改函数都有个逻辑错误 C++课程设计-单链表——学生信息管理系统全文共9页,当前为第6页。全部修改后,程序...
c++实现的 可保存的 mfc 程序 ————————————
清晰完整的数据结构与算法——面向对象C++设计模式,需要的朋友可以下载
面向对象程序设计,即C++语言,类。函数的参数是类的对象引用,文件里面有详细的注释。
3、请设计一个拷贝构造函数,实现将参数的值全部传递给当前对象,同时输出“拷贝构造函数被调用” 4、请设计一个析构函数,同时输出“析构函数被调用” 5、设计一个成员函数 int dayDiff(CTime t) ,用于计算...
C++中可变参数函数的源码,文件很小,但可以供参考用,还特地写了一个类作为可变参数函数的参数。可以直接编译。
文章对可变参数函数的参数传递机制进行了剖析, 给出了准确、灵活设计可变参数函数的另一种方法