先说结论:C++的类成员函数和C函数实质是一样的,只是C++类成员函数多了隐藏参数this。

通过本文的演示,可以看见这背后的一切,完全可C函数方式调用C++类普通成员函数和C++类虚拟成员函数。

为了实现C函数方式调用C++类成员函数,准备两个文件:。

1) 被调用的C++类成员函数源代码文件aaa.cpp

#include <stdio.h> // fprintf

class X

{

public:

void xxx();

private:

int m;

int n;

};

void X::xxx() // bbb.cpp完全以C函数方式调用类X的成员函数xxx

{

printf("m=%d, n=%d\n", m, n);

}

把aaa.cpp编译成共享库:

$ g++ -g -o libaaa.so aaa.cpp -fPIC -shared

2) 调用的C++类成员函数源代码文件bbb.cpp

#include <dlfcn.h> // dlopen

#include <libgen.h> // basename

#include <stdio.h> // fprintf

#include <stdlib.h> // exit

#include <string.h> // strdup

struct X // 对应于aaa.cpp中类X

{

int m;

int n;

};

// 定义C风格函数指针XXX,使用前让它指向类X的成员函数xxx

typedef void (*XXX)(struct X*); // 参数实为aaa.cpp中类X的this指针

// 需要指定一个命令行参数argv[1],

// 值为aaa.cpp中类X的成员函数xxx的名字,

// 因为C++编译器会对类X的成员函数xxx名字编码,所以实际名字不会是xxx,

// 本文测试环境xxx编码后的名为_ZN1X3xxxEv,

// 不同环境可能有区别,特别是不同编译器通常不同,因为C++标准未对这个做规范。

int main(int argc, char* argv[])

{

char* prog = strdup(argv[0]);

if (argc != 2) {

fprintf(stderr, "Usage: %s symbol-name\n", basename(prog));

exit(1);

}

// 为测试方便,两个文件放在同一目录下,省去设置LD_LIBRARY_PATH

const char* so = "./libaaa.so";

void* h = dlopen(so, RTLD_NOW); // 加载类X所在共享库文件

if (NULL == h) {

fprintf(stderr, "dlopen %s failed: %s\n", so, dlerror());

exit(1);

}

XXX xxx = (XXX)dlsym(h, argv[1]); // 取和类X的类成员函数xxx的函数地址,以便可以调用它

if (NULL == xxx) {

fprintf(stderr, "dlsym %s failed: %s\n", argv[1], dlerror());

exit(1);

}

// 第1组测试数据

struct X x1;

x1.m = 19;

x1.n = 18;

(*xxx)(&x1); // 这里完全以C函数方式调用类X的类成员函数xxx

// 第2组测试数据

struct X x2;

x2.m = 2019;

x2.n = 2018;

(*xxx)(&x2); // 这里完全以C函数方式调用类X的类成员函数xxx

// 第3组测试数据

x2.m = 29;

x2.n = 28;

(*xxx)(&x2); // 这里完全以C函数方式调用类X的类成员函数xxx

return 0;

}

把bbb.cpp编译成可执行程序:

$ g++ -g -o bbb bbb.cpp -ldl

执行bbb,看看效果,运行结果和预计完全一致:

$ ./bbb _ZN1X3xxxEv

m=19, n=18

m=2019, n=2018

m=29, n=28

以更优雅方式运行:

$ ./bbb `nm libaaa.so | awk /xxx/'{print $3}'`

m=19, n=18

m=2019, n=2018

m=29, n=28

对于类虚拟成员函数,做法是一样的,只是bbb.cpp中的struct X定义得改一下,因为有虚拟函数的类的头一个指针大小为指向虚拟函数表的指针。

包含虚拟函数的aaa.cpp:

#include <stdio.h> // fprintf

class X

{

public:

virtual void xxx(); // 编码后的函数名和是否为虚拟函数无关

private:

int m;

int n;

};

void X::xxx() // bbb.cpp完全以C函数方式调用类X的成员函数xxx

{

printf("m=%d, n=%d\n", m, n);

}

bbb.cpp只需修改struct X的定义:

struct X // 对应于aaa.cpp中类X

{

void* p; // 对应aaa.cpp中类X的虚拟函数表指针

int m;

int n;

};

其它操作步骤完全相同,运行同样可得到预期的结果。

实战C++对象模型之成员函数调用的更多相关文章

  1. C++对象模型的那些事儿之六:成员函数调用方式

    前言 C++的成员函数分为静态函数.非静态函数和虚函数三种,在本系列文章中,多处提到static和non-static不影响对象占用的内存,而虚函数需要引入虚指针,所以需要调整对象的内存布局.既然已经 ...

  2. 【深度探索c++对象模型】Function语义学之成员函数调用方式

    非静态成员函数 c++的设计准则之一就是:非静态成员函数至少和一般的非成员函数有相同的效率.编译器内部已将member函数实体转换为对等的nonmember函数实体. 转化步骤: 1.改写函数原型以安 ...

  3. C++对象模型:成员变量<一>非静态成员变量

    非静态成员变量,分别两种可能,要么类自定义,要么继承而来.根据<深度探索C++对象模型>的解读. class X { private: int x,y,z; }; 在这个类中,有三个私有成 ...

  4. 第50 课C++对象模型分析——成员变量(上)

    C++对象模型,其实就是C++中的对象在内存中是如何排布的.C++中的对象包含了成员变量和成员函数,其实就是研究C++中的类对象它的成员变量和成员函数在内存中是如何排布的. 回归本质class 是一种 ...

  5. 类成员函数调用delete this会发生什么呢?

    有如下代码 class myClass { public: myClass(){}; ~myClass(){}; void foo() { delete this; } }; int main() { ...

  6. C++并发类成员函数调用(练习1)

    一般类成员函数开线程格式 std::thread t1(&类名::函数,&实例化对象,参数....) ||std::thread t1(std::bind(&&类名:: ...

  7. C++对象模型——对象成员的效率 (Object Member Efficiency)(第三章)

    3.5 对象成员的效率 (Object Mem ber Efficiency) 以下某个測试,目的在測试聚合(aggregation).封装(encapsulation),以及继承(Inheritan ...

  8. 第50 课C++对象模型分析——成员函数(上)

    类中的成员函数位于代码段中调用成员函数时对象地址作为参数隐式传递成员函数通过对象地址访问成员变量C++语法规则隐藏了对象地址的传递过程 #include<iostream> #includ ...

  9. Java类的成员函数调用顺序

    class A { public A() { System.out.println("----------A 构造-------------"); } static void sb ...

随机推荐

  1. spring cloud gateway - RequestRateLimiter

    1. Official website 5.7 RequestRateLimiter GatewayFilter Factory The RequestRateLimiter GatewayFilte ...

  2. RabbitMQ--windows10环境下的RabbitMQ安装步骤(转)

    https://blog.csdn.net/weixin_39735923/article/details/79288578

  3. leetCode26.删除排序数组中的重复项

    给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度. 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成. 示例 1 ...

  4. 某大型跨境电商JVM调优总结

    前提:某大型跨境电商业务发展非常快,线上机器扩容也很频繁,但是对于线上机器的运行情况,特别是jvm内存的情况,一直没有一个统一的标准来给到各个应用服务的owner.经过618大促之后,和运维的同学讨论 ...

  5. 关于java字节码框架ASM的学习

      一.什么是ASM ASM是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能.ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为 ...

  6. CUDA compiler driver nvcc 散点 part 1

    ▶ 参考[https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html] ▶ nvcc 预定义的宏 __NVCC__ // 编译 ...

  7. PHP判断手机、电脑访问

    /*判断用户是手机访问还是电脑访问*/$useragent = $_SERVER['HTTP_USER_AGENT']; if (preg_match('/(android|bb\d+|meego). ...

  8. thymeleaf注入springboot

    thymeleaf注入springboot需要引入jar: <dependency> <groupId>org.springframework.boot</groupId ...

  9. SQL 中的语法顺序与执行顺序

    FROM : HOME SQL 是一种声明式语言 SQL 语言是为计算机声明了一个你想从原始数据中获得什么样的结果的一个范例,而不是告诉计算机如何能够得到结果. SQL 语言声明的是结果集的属性,计算 ...

  10. TensorFlow学习之二

    二.常用操作符和基本数学函数 大多数运算符都进行了重载操作,使我们可以快速使用 (+ - * /) 等,但是有一点不好的是使用重载操作符后就不能为每个操作命名了. 1  算术操作符:+ - * / % ...