实战C++对象模型之成员函数调用
先说结论: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++对象模型之成员函数调用的更多相关文章
- C++对象模型的那些事儿之六:成员函数调用方式
前言 C++的成员函数分为静态函数.非静态函数和虚函数三种,在本系列文章中,多处提到static和non-static不影响对象占用的内存,而虚函数需要引入虚指针,所以需要调整对象的内存布局.既然已经 ...
- 【深度探索c++对象模型】Function语义学之成员函数调用方式
非静态成员函数 c++的设计准则之一就是:非静态成员函数至少和一般的非成员函数有相同的效率.编译器内部已将member函数实体转换为对等的nonmember函数实体. 转化步骤: 1.改写函数原型以安 ...
- C++对象模型:成员变量<一>非静态成员变量
非静态成员变量,分别两种可能,要么类自定义,要么继承而来.根据<深度探索C++对象模型>的解读. class X { private: int x,y,z; }; 在这个类中,有三个私有成 ...
- 第50 课C++对象模型分析——成员变量(上)
C++对象模型,其实就是C++中的对象在内存中是如何排布的.C++中的对象包含了成员变量和成员函数,其实就是研究C++中的类对象它的成员变量和成员函数在内存中是如何排布的. 回归本质class 是一种 ...
- 类成员函数调用delete this会发生什么呢?
有如下代码 class myClass { public: myClass(){}; ~myClass(){}; void foo() { delete this; } }; int main() { ...
- C++并发类成员函数调用(练习1)
一般类成员函数开线程格式 std::thread t1(&类名::函数,&实例化对象,参数....) ||std::thread t1(std::bind(&&类名:: ...
- C++对象模型——对象成员的效率 (Object Member Efficiency)(第三章)
3.5 对象成员的效率 (Object Mem ber Efficiency) 以下某个測试,目的在測试聚合(aggregation).封装(encapsulation),以及继承(Inheritan ...
- 第50 课C++对象模型分析——成员函数(上)
类中的成员函数位于代码段中调用成员函数时对象地址作为参数隐式传递成员函数通过对象地址访问成员变量C++语法规则隐藏了对象地址的传递过程 #include<iostream> #includ ...
- Java类的成员函数调用顺序
class A { public A() { System.out.println("----------A 构造-------------"); } static void sb ...
随机推荐
- 搭建基于MySQL的读写分离工具Amoeba
搭建基于MySQL的读写分离工具Amoeba: Amoeba工具是实现MySQL数据库读写分离的一个工具,前提是基于MySQL主从复制来实现的: 实验环境(虚拟机): 主机 角色 10.10.10.2 ...
- leetcode141
/** * Definition for singly-linked list. * public class ListNode { * public int val; * public ListNo ...
- ABAP-表中数据的横向累加
GLT0 等表中数据的横向累加 ——塞依SAP培训 在 ABAP 的 GLT0.FAGLFLEXT 等余额表中,用诸如 TSL01.TSL02 …… TSL16 等字段记录了 16 个期间的发生额 ...
- Python介绍与安装
Python 是一种面向对象的解释型程序设计语言,支持支持面向过程.函数式和面向对象编程.另外,Python可以在Windows.UNIX等多个操作系统上使用. 为什么学编程 编程是一种工具,可以实现 ...
- Match-----Correlation-----find_ncc_model_exposure
* This example program shows how to use HALCON's correlation-based* matching. In particular it demon ...
- 《CSAPP》虚拟存储器
虚拟存储器与物理存储器 虚拟存储器(VM)被组织为一个由存放在磁盘上的N个连续的字节大小的单元组成的数组.每一个字节都有一个唯一的虚拟地址,这个唯一的虚拟地址作为数组的索引.磁盘上的数组内容被缓存在主 ...
- Linux基本的操作
一.为什么我们要学习Linux 相信大部分人的PC端都是用Windows系统的,那我们为什么要学习Linux这个操作系统呢???Windows图形化界面做得这么好,日常基本使用的话,学习成本几乎为零. ...
- protobuf shutdownprotobuflibrary的时候crash,释放的指针出错
往往是多个子项目中有多次链接使用. 解决方法: 1. 使用静态库. 2. issure中有说2.6.1还未允许多次释放,建议使用3.4.x版本. 参考: https://github.com/prot ...
- [Java核心技术笔记]并发
Runnable Runnable接口是函数式接口 //Runnable接口 public interface Runnable { void run(); } //用lambda表达式创建实例 Ru ...
- [leetcode]45. Jump Game II青蛙跳(跳到终点最小步数)
Given an array of non-negative integers, you are initially positioned at the first index of the arra ...