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++成员不通过对象调用的直接调用写法的更多相关文章

  1. Java学习笔记11---静态成员变量、静态代码块、成员变量及构造方法的初始化或调用顺序

    当创建一个对象时,各种成员变量及构造方法的初始化或调用顺序是怎样的呢? (1).如果类尚未加载,则先初始化静态成员变量和静态代码块,再初始化成员变量,最后调用相应的构造方法: (2).如果类已经加载过 ...

  2. 12.C++-构造函数与析构函数调用顺序,const成员函数,const对象

    单个对象创建时,构造函数的调用顺序 1.首先判断该对象的类是否拥有父类,若有则先调用父类的构造函数 2.判断该对象的成员是否是其它类的成员,若是则调用成员变量的构造函数(调用顺序和声明顺序相同) 3. ...

  3. Java中 对象的创建于调用

    Main方法是程序的主入口,想要用某个方法必须在main方法中调用 创建对象: 类名 对象名 = new 类名(); 使用对象访问类中的成员: 对象名.成员变量: 对象名.成员方法(); 成员变量的默 ...

  4. PHP将在对象被销毁前调用这个函数.它称为析构函数

    -构造函数和析构函数 如果你在一个类中声明一个函数,命名为__construct,这个函数将被当成是一个构造函数并在建立一个对象实例时被执行.清楚地说,__是两个下划线.就像其它任何函数一样,构造函数 ...

  5. C#之转换两个不同类型但是成员相同的对象

    /// <summary> /// 转换两个不同类型但是成员相同的对象 /// </summary> /// <typeparam name="T"& ...

  6. 错误笔记 对象为null时调用改对象的方法会报错

    对象为null时调用改对象的方法会报错

  7. 【C++】私有数据成员不能用对象去访问吗

    首先,必须清楚的是private和public限定的是类而不是对象.因此,在成员函数中访问同类对象的私有成员是完全可以的. 所以,某些教材上所说的“私有数据成员不能用对象去访问”是欠妥当的. 比如,如 ...

  8. 反射-优化及程序集等(用委托的方式调用需要反射调用的方法(或者属性、字段),而不去使用Invoke方法)

    反射-优化及程序集等(用委托的方式调用需要反射调用的方法(或者属性.字段),而不去使用Invoke方法)   创建Delegate (1).Delegate.CreateDelegate(Type, ...

  9. 《oracle每天一练》触发器不能调用或间接调用COMMIT,ROLLBACK等DCL语句

    触发器不能调用或间接调用COMMIT,ROLLBACK等DCL语句 在触发器中不能运行 ddl语句和commit,rollback语句 ddl语句:DDL语句用语定义和管理数据库中的对象,如Creat ...

随机推荐

  1. JavaScript Array 对象的方法,比如push和unshift

    https://www.runoob.com/jsref/jsref-obj-array.html js数组与字符串的相互转换 一.数组转字符串 需要将数组元素用某个字符连接成字符串,示例代码如下: ...

  2. css3上下翻页效果

    翻页效果显示当前时间 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> &l ...

  3. 用正则表达式模仿Mustache插件的功能

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...

  4. 学习环境配置:Manjaro、MSYS2以及常见软件

    0.前言 在说Manjaro之前,要先说一下Linux发行版.对于各大发行版而言,内核只有版本的差异,最重要的区别就是包管理系统.常见的包管理系统包括:Pacman,Apt , Yum和Portage ...

  5. python安装在windows server2008,故障排除

    python安装在windows server2008,故障排除   我也在虚拟机上的win2008安装python2.7.9多次回滚了.搜了一通 Windows Server 2003/2008无法 ...

  6. Go语言调度器之主动调度(20)

    本文是<Go语言调度器源代码情景分析>系列的第20篇,也是第五章<主动调度>的第1小节. Goroutine的主动调度是指当前正在运行的goroutine通过直接调用runti ...

  7. luogu P3147 [USACO16OPEN]262144

    题目描述 Bessie likes downloading games to play on her cell phone, even though she doesfind the small to ...

  8. mac与linux服务器之间使用ssh互通有无

    1. 在mac上没有找到好用的shell图形界面的软件,但也是有办法的,使用ssh公钥达到互相有无目的 2.场景是mac连A(linux,以下简称A)服务器 3.登陆mac shell ,按comma ...

  9. 基于WPF系统框架设计(7)-TextBox/PasswordBox在ViewModel中支持回车命令

    应用场景 我现在做一个系统登录功能,要求在PasswordBox上输完密码后回车,能够响应Enter事件,并执行ViewModel中对应的方法.如果登录成功则隐藏当前窗口显示主窗体,登录失败则焦点返回 ...

  10. iOS7开发技巧

    和任何新的iOS版本一样,有着一堆堆的新技巧和修改需要处理.有些我并不会立即遇到,所以这篇文章并不是一套完整技巧汇总.只是分享一些我碰巧遇到的问题. 如果你有任何更多的发现,可以发Twitter或者e ...