C++成员不通过对象调用的直接调用写法
C++成员不通过对象调用(.或->方式)的另类(C式)调用写法
#include <iostream>
using namespace std; /*
我们知道,成员函数和普通函数最大的区别就是成员函数包含一个隐藏的参数this指针,用来表明成员函
数当前作用在那一个对象实例上。 根据调用约定(Calling Convention)的不同,成员函数实现this指针的方式也不同。 1. 如果使用__thiscall调用约定,那么this指针保存在寄存器ECX中,VC编译器缺省情况下就是这样的。
2. 如果是__stdcall或__cdecl调用约定,this指针将通过栈进行传递,且this指针是最后一个被压入栈
的参数,相当于编译器在函数的参数列表中最左边增加了一个this参数。
*/
class Base {
public:
virtual void f() { cout << "Base::f()" << endl; }
virtual void g() { cout << "Base::g()" << endl; }
virtual void h() { cout << "Base::h()" << endl; } virtual void foo(Base *pThis) {
pThis->hello(this);
}
virtual void hello(Base *pThis) {
pThis->h();
} //virtual void __stdcall hello(Base *pThis) {} //成员函数指定了__stdcall调用约定
}; int test()
{
typedef void(*Fun)(); Base *b = new Base;
cout << *(int*)(&b) << endl; //虚函数表的地址存放在对象最开始的位置 Fun funf = (Fun)(*(int*)*(int*)b);
Fun fung = (Fun)(*((int*)*(int*)b + ));
Fun funh = (Fun)(*((int*)*(int*)b + )); /************************************************************************/
/* 调用内部无this参与的成员(包括变量和方法)的对象方法 */
/************************************************************************/ //如果下面三个方法里,没有用到与对象相关的成员可以不用为ecx赋值,否则会出错
funf();
fung();
funh(); /************************************************************************/
/* 调用内部有this参与的成员(包括变量和方法)的对象方法 */
/************************************************************************/ //少了__stdcall(注意位置),栈会不平衡了:本来c++默认是thiscall,如果不要,vs编译器会让调用者平衡栈,即多了一句 add esp, 4
typedef void(__stdcall *Fun_Base)(Base*);
Fun_Base foo = Fun_Base(*((int*)*(int*)b + )); //就是多增加这句,因为编译器对c++默认采用thiscall
_asm{
mov ecx, dword ptr[b]
} foo(b);//里面用到this了,不给ecx赋值,this就不对 /************************************************************************/
/* 调用内部有this参与的成员(包括变量和方法)的对象方法,纯汇编版本 */
/************************************************************************/ //尝试调用虚函数表的第四个方法
_asm{
//同上,如果所call的方法里,没有用到与对象相关的成员可以不用为ecx赋值,否则会出错
mov ecx, dword ptr[b] push ecx //一个入参 //mov ecx, b或mov eax, [b],表达同一个意思,vs最后都是mov ecx, dword ptr[b];
//实质是mov eax, [EBP-04h],结果表现为ecx=b,即从栈上获取指针b的值,而不是*b(即*(int*)b == ptr_vftable)的值。
mov eax, [b] //获取对象指针
mov eax, [eax] //虚函数表首地址,即对象的开始处,ptr_vftable = [eax + 0] = [ecx] = [this_of_b] = [b]
call [eax + 0x0c] //调用虚函数表的第四个,某虚函数表里第N个方法(x86),[ptr_vftable + (N - 1) * 4] = [[eax]+(N-1)*4]
} /************************************************************************/
/* 正常的调用 */
/************************************************************************/ /*
00EF9886 8B 45 F4 mov eax, dword ptr[b]
00EF9889 50 push eax
00EF988A 8B 4D F4 mov ecx, dword ptr[b]
00EF988D 8B 11 mov edx, dword ptr[ecx]
00EF988F 8B 4D F4 mov ecx, dword ptr[b]
00EF9892 8B 42 0C mov eax, dword ptr[edx + 0Ch]
00EF9895 FF D0 call eax
*/
b->foo(b); //http://blog.csdn.net/haoel/article/details/1948051/
/*
虚函数表的结束结点,就像字符串的结束符“/0”一样,其标志了虚函数表的结束。
这个结束标志的值在不同的编译器下是不同的。 1. 在WinXP+VS2003下,这个值是NULL。
2. 而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下,
2.1 这个值是如果1,表示还有下一个虚函数表,
2.2 如果值是0,表示是最后一个虚函数表。
*/
cout << (Fun)(*((int*)*(int*)b + )); // 最后一个位置为0,表明虚函数表的结束 return ;
}
C++虚函数介绍
1. 先从C++看看下虚函数表和多态表现 http://blog.csdn.net/haoel/article/details/1948051/
2. 有虚函数的类的大小 http://blog.csdn.net/hackbuteer1/article/details/7883531
3. 再从汇编看,类的继承,虚函数表的形成,表放在哪个地址等 http://www.pediy.com/kssd/pediy10/60538.html
虚函数表的形成,是在类构造函数里,对实例对象首地址里存放的虚函数数组进行修改实现的。
class Child :public Base{
public:
virtual void vf1(){cout<<"I'm in sub Class.";}
virtual void vf2(){cout<<"I'm in sub Class.";}
//...
}
.text .rdata?
class Child内存 这个类的所有对象所共有的
+-------------+ +-----+-----+-----+-----+----+
| ptr_vftable | -------> | vf1 | vf2 | vf3 | ... |end?|
+-------------+ +-----+-----+-----+-----+----+
| |
|other members|
| |
+-------------+
逆向C++(中文版)
http://wenku.baidu.com/link?url=bjLVj2eqfe29_Edzi99MBGJeoCtVaHDXj-3r4s4lm771BAQnJ0WIUaQywPZgGq3Yz_uU9yh-B0V6q5SFMUhRo0t436BUnUdaHuhpwERvLvC
C++多态性:
GoF著作中未提到的设计模式(4):Double Dispatch
http://www.cnblogs.com/west-link/archive/2011/07/26/2116887.html
http://en.wikipedia.org/wiki/Double_dispatch
C++成员不通过对象调用的直接调用写法的更多相关文章
- Java学习笔记11---静态成员变量、静态代码块、成员变量及构造方法的初始化或调用顺序
当创建一个对象时,各种成员变量及构造方法的初始化或调用顺序是怎样的呢? (1).如果类尚未加载,则先初始化静态成员变量和静态代码块,再初始化成员变量,最后调用相应的构造方法: (2).如果类已经加载过 ...
- 12.C++-构造函数与析构函数调用顺序,const成员函数,const对象
单个对象创建时,构造函数的调用顺序 1.首先判断该对象的类是否拥有父类,若有则先调用父类的构造函数 2.判断该对象的成员是否是其它类的成员,若是则调用成员变量的构造函数(调用顺序和声明顺序相同) 3. ...
- Java中 对象的创建于调用
Main方法是程序的主入口,想要用某个方法必须在main方法中调用 创建对象: 类名 对象名 = new 类名(); 使用对象访问类中的成员: 对象名.成员变量: 对象名.成员方法(); 成员变量的默 ...
- PHP将在对象被销毁前调用这个函数.它称为析构函数
-构造函数和析构函数 如果你在一个类中声明一个函数,命名为__construct,这个函数将被当成是一个构造函数并在建立一个对象实例时被执行.清楚地说,__是两个下划线.就像其它任何函数一样,构造函数 ...
- C#之转换两个不同类型但是成员相同的对象
/// <summary> /// 转换两个不同类型但是成员相同的对象 /// </summary> /// <typeparam name="T"& ...
- 错误笔记 对象为null时调用改对象的方法会报错
对象为null时调用改对象的方法会报错
- 【C++】私有数据成员不能用对象去访问吗
首先,必须清楚的是private和public限定的是类而不是对象.因此,在成员函数中访问同类对象的私有成员是完全可以的. 所以,某些教材上所说的“私有数据成员不能用对象去访问”是欠妥当的. 比如,如 ...
- 反射-优化及程序集等(用委托的方式调用需要反射调用的方法(或者属性、字段),而不去使用Invoke方法)
反射-优化及程序集等(用委托的方式调用需要反射调用的方法(或者属性.字段),而不去使用Invoke方法) 创建Delegate (1).Delegate.CreateDelegate(Type, ...
- 《oracle每天一练》触发器不能调用或间接调用COMMIT,ROLLBACK等DCL语句
触发器不能调用或间接调用COMMIT,ROLLBACK等DCL语句 在触发器中不能运行 ddl语句和commit,rollback语句 ddl语句:DDL语句用语定义和管理数据库中的对象,如Creat ...
随机推荐
- 【07】react 之 生命周期
阅读目录(Content) 实例化 getDefaultProps getInitialState componentWillMount render componentDidMount 存在期 co ...
- bzoj 1367 - sequence
Description 给定一个序列\(t_1,t_2,\cdots,t_n\),求一个递增序列\(z_1<z_2<...<z_n\), 使得 \(R=|t_1−z_1|+|t_2− ...
- EasySlider-最简洁的JQuery滚动插件 可控制滚动
原文发布时间为:2010-05-05 -- 来源于本人的百度文章 [由搬家工具导入] Easy Silder是由Alen Grakalic开发的基于JQuery的滚动插件,它支持以下功能: 1.自动滚 ...
- updatepanel的使用【他人经验+原创 完整例子】
原文发布时间为:2009-05-16 -- 来源于本人的百度文章 [由搬家工具导入] 刚上传的代码,不知道能不能下载:[源码包含所有ajax控件的简单使用] http://www.xmaspx.com ...
- 线程间操作无效: 从不是创建控件“textBox4”的线程访问它
原文发布时间为:2009-03-30 -- 来源于本人的百度文章 [由搬家工具导入] public long str1, str2, str3, str4, str5, sum=0,sum1=0; p ...
- 给页面上所有的a标签增加随机数每次点击保证最新
$(document).click(function(){ $("a").each(function(){ if($(this).parent().parent().hasClas ...
- 浅谈前端性能优化(PC版)
前端的性能优化是一个很宽泛的概念,最终目的都是为了提升用户体验,改善页面性能.面试的时候经常会遇到问谈谈性能优化的手段,这个我分几大部分来概述,具体细节需要自己再针对性的去搜索,只是提供一个索引(太多 ...
- Codeforces Round #454 C. Shockers【模拟/hash】
C. Shockers time limit per test 2 seconds memory limit per test 256 megabytes input standard input o ...
- 【Kafka】《Kafka权威指南》——写数据
不管是把 Kafka 作为消息队列.消息.总线还是数据存储平台来使用 ,总是需要有一个可以往 Kafka 写入数据的生产者和一个可以从 Kafka读取数据的消费者,或者一个兼具两种角 色的应用程序. ...
- mysql null与not null
http://blog.csdn.net/fwkjdaghappy1/article/details/7703974/ NULL表示 值 可为NULL,并非空的意思, NOT NULL表示 值不能为N ...