使用函数指针调用C++虚函数

摘要:
2.虚函数,一个可以被子类重写的C++成员函数。它由虚拟函数表实现。3.虚拟函数表指针,指向虚拟函数表的第一个地址的指针,通常放置在类实例的前4个字节中。它可以找到的虚拟函数表,然后找到一个虚拟函数。但是第一个结果是随机数,第二个结果是100。可以看出,第二种方法确实在这个指针通过程序集中传递。第一个随机数的原因是,当类模板加载到内存中时,它的这个指针是模板的第一个地址。显然,它没有初始化。回到前面的问题,我仍然在纠结如何传递参数!

基本概念:

  1. 函数指针,一个地址指针变量,其值指向代码区的某个函数首地址。

  2. 虚函数,可以被子类覆写的C++成员函数。由虚函数表实现。

  3. 虚函数表指针(vpt),指向虚函数表首地址的指针,一般放在类实例的首4字节(x86系统)。通过它能找的虚函数表,进而能找的某个虚函数。

函数调用:

  1. C++函数的普通调用方式:      

class Person {
private:
   int value;
public:
   Person():value(100){} 
   virtual int getValue(int a) {
       return value + a;         
   }  
};

void main() {
  Person p;
  int result = p.getValue(30);    // result 为 130
}

  2. 使用普通函数指针调用:

class Person {
private:
   int value;
public:
   Person():value(100){} 
   virtual int getValue(int a) {
       return value + a;         
   }  
};

typedef int (Person::PFUNC) (int);    // 定义函数指针类型

void main() {
  PFUNC pf = &Person::getValue;      // 定义变量
  Person p;
  // int result = p.getValue(30);    
  int result = p.*pf(30);               // result 为 130
  result = &p->*pf(30);                // 语法与上句一样
}

  3. 使用函数指针外部调用

class Person {
private:
   int value;
public:
   Person():value(100){} 
   virtual int getValue(int a) {
       return value + a;         
   }  
};

typedef int (PFUNC) (int);    // 定义函数指针类型

void main() {
  Person p, *pp;
  pp = &p;
  PFUNC pf = (PFUNC)**(int **)pp;  // 通过虚函数表获得虚函数pf
  int result = pf(30);               // 崩了,因为没有this指针
}

/*以下是老师提供的代码,但是也崩了*/
void main() {
  Person p, *pp;
  pp = &p;
  PFUNC pf = (PFUNC)**(int **)pp;    // 通过虚函数表获得虚函数pf
  __asm mov ecx, pp           // 传入this指针
  int result = pf(30);               // 还是崩了
}

 

对于第三种方式,this指针传不进去,然后一直报错:

  The value of ESP was not properly saved across a function call.  This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.

  如果我把getValue函数换成无参的,两种方法都可以调用。但是结果第一种是随机数,第二种是100。可见第二种通过汇编方式确实传进去了this指针,(在有多继承时这个this指针一般会做调整的),第一种随机数的原因是它的this指针是类模板装载入内存时的模板首地址,显然它没有初始化。

  回到上题,我还在纠结于怎么传参!

免责声明:文章转载自《使用函数指针调用C++虚函数》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇WebRTC通信流程海康定制RTMP推流摄像头如何接入EasyDSS视频直播点播平台?下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

python迭代器与生成器及yield

一、迭代器(itertor) 1.可迭代: 在Python中如果一个对象有__iter__()方法或__getitem__()方法,则称这个对象是可迭代的(iterable)。 其中__iter__()方法的作用是让对象可以用“for ... in...”方式来循环遍历,_getitem_()方法是让对象可以通过“实例名[index]”的方式访问实例中的元...

Go-结构体

Go语言中没有“类”的概念,也不支持“类”的继承等面向对象的概念。Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的扩展性和灵活性。 类型别名和自定义类型 自定义类型 在Go语言中有一些基本的数据类型,如string、整型、浮点型、布尔等数据类型,Go语言中可以使用type关键字来定义自定义类型。 自定义类型是定义了一个全新的类型。我们可以基于内置的...

C/C++获取系统当前时间

C/C++获取系统当前时间   C库中与系统时间相关的函数定义在<time.h>头文件中, C++定义在<ctime>头文件中。 一、time(time_t*)函数 函数定义如下: time_t time (time_t* timer); 获取系统当前日历时间 UTC 1970-01-01 00:00:00开始的unix时间戳参数:...

使用lockbits方法处理图像(转)

   许多图像处理任务即时是最简单的文件类型转换,例如从32位深度到8位深度的格式转化,直接获得像素阵列要比使用GetPixel和SetPixel等方法的效率高得多。         你可能会发现DotNet采用托管机制,大多数情况下微软会推荐你使用托管代码,理由是便捷和安全。实际应用中,直接操作内存中的数据块是很少见的,尽管如此,图像处理恰恰是这类为数...

c++对象初始化中 ZeroMemory、memset、直接赋0的区别

首先是ZeroMemory和memset的区别: 1、ZeroMemory是微软的SDK提供的,memset属于C Run-time Library提供的。因此ZeroMemory只能用于Windows系统,而memset还可用于其他系统。 2、ZeroMemory是一个宏,只是用于把一段内存的内容置零,内部其实是用 memset实现的,而memset除...

open与fopen 文件描述符与文件指针

首先说一下文件描述符与文件指针区别: 文件描述符:在linux系统中打开文件就会获得文件描述符,它是个很小的正整数。每个进程在PCB(Process Control Block)中保存着一份文件描述符表,文件描述符就是这个表的索引,每个表项都有一个指向已打开文件的指针。 文件指针:C语言中使用文件指针做为I/O的句柄。文件指针指向进程用户区中的一个被称为...