用C表达面向对象语言的机制2——颠覆你对方法调用的看法!
用C表达面向对象语言的机制2——颠覆你对方法调用的看法!
源代码在文末。推荐阅读本文PDF版,格式更好看。
在上一篇《用C表达面向对象语言的机制——C#版》中,我们获知了如何用C表达面向对象语言的机制,证明了面向对象语言是对面向过程语言的封装。今天有幸看到《颠覆你对方法调用的看法!》,于是继续用C来模拟此文中的代码,看看“颠覆”的背后是什么。
1. 目标
本文展示用C的union来模拟C#的一些代码的写法。
2. 用union代替FieldOffset
例如如下的C#代码。
Manager class Derived : Base
{
int d = 11;
public new void Print()
{
Console.WriteLine("in derived ({0})", this.d);
}
} [StructLayout(LayoutKind.Explicit)]
class Manager
{
[FieldOffset(0)]
public Base b = new Base();
[FieldOffset(0)]
public Derived derived;
}
如果想用C来实现类似的使用方式,应该如何写呢?
1) 用union代替FieldOffset
用C的union代替FieldOffset等属性。
FrancisYoungBasetypedef struct _FrancisYoungBase
{
// basic info
Metadata * metaInfo;
// fields
int b;
// virtual methods } FrancisYoungBase; // type id
static int FrancisYoungBaseTypeId = 11; // method declarations // the new method
FrancisYoungBase * NewFrancisYoungBase()
{
// alloc for space
FrancisYoungBase * pResult = (FrancisYoungBase *)malloc(sizeof(FrancisYoungBase)); // initialize basic info
pResult->metaInfo = NewMetadata(
pResult,
FrancisYoungBaseTypeId,
NULL);
// initialize fields
pResult->b = 10; // initialize virtual methods // return result
return pResult;
} void Print4FrancisYoungBase(FrancisYoungBase * pThis)
{
if (pThis == NULL) { return;/* throw exception in C# */ } printf("in base (%d)\n", pThis->b);
}
FrancisYoungDerivedtypedef struct _FrancisYoungDerived
{
// basic info
Metadata * metaInfo; // fields
int d;
// virtual methods } FrancisYoungDerived; // type id
static int FrancisYoungDerivedTypeId = 12; // method declarations
void Print4FrancisYoungDerived(FrancisYoungDerived * pThis); // the new method
FrancisYoungDerived * NewFrancisYoungDerived()
{
// initialize base class
FrancisYoungBase * pBase = NewFrancisYoungBase(); // alloc for space
FrancisYoungDerived * pResult = (FrancisYoungDerived * )malloc(sizeof(FrancisYoungDerived));
// initialize basic info
pResult->metaInfo = NewMetadata(
pResult,
FrancisYoungDerivedTypeId,
pBase->metaInfo); // initialize fields
pResult->d = 11; // initialize virtual methods // return result
return pResult;
} void Print4FrancisYoungDerived(FrancisYoungDerived * pThis)
{
if (pThis == NULL) { return;/* throw exception in C# */ } printf("in derived (%d)\n", pThis->d);
}
FrancisYoungManagertypedef struct _FrancisYoungManager
{
Metadata * metaInfo;
union
{
FrancisYoungBase * pBase;
FrancisYoungDerived * pDerived;
} obj;
} FrancisYoungManager; // type id
static int FrancisYoungManagerTypeId = 13; // method declarations // the new method
FrancisYoungManager * NewFrancisYoungManager()
{
// initialize base class // alloc for space
FrancisYoungManager * pResult = (FrancisYoungManager * )malloc(sizeof(FrancisYoungManager));
// initialize basic info
pResult->metaInfo = NewMetadata(
pResult,
FrancisYoungManagerTypeId,
NULL); // initialize fields
pResult->obj.pBase = NewFrancisYoungBase(); // initialize virtual methods // return result
return pResult;
}
就是说,union实现了将父类和子类指针都指向父类的实例的功能。
2) 使用Manager类
C#版的Manager,其典型的使用方式如下。
Manager m = new Manager();
m.derived.Print();// In Derived
对应的C代码是什么样的?
FrancisYoungManager * m = NewFrancisYoungManager();
Print4FrancisYoungDerived(m->obj.pDerived);/* In Derived */
与C#版的使用方法异曲同工。
从C版代码可以看到,编译器根据pDerived的类型,选择了调用pDerived的类型中的方法。观察对C#转换为C的代码,可以发现,本质上同名的Print方法,对编译器而言的代号是不同的,所以编译器根据pDerived的类型,可以立即确定调用哪个方法。
3. 变为虚函数之后
下面展示将上文中的函数变为虚函数后的处理。
C#代码如下。
FrancisYoungManagerVirtual [StructLayout(LayoutKind.Explicit)]
class ManagerVirtual
{
[FieldOffset(0)]
public BaseVirtual b = new BaseVirtual(); [FieldOffset(0)]
public DerivedVirtual derived;
} class BaseVirtual
{
int b = 12;
public virtual void Print()
{
Console.WriteLine("in base ({0})", this.b);
}
} class DerivedVirtual : BaseVirtual
{
int d = 13;
public override void Print()
{
Console.WriteLine("in derived ({0})", this.d);
}
}
3) 用函数指针代替虚函数
仍然用上一篇文章的方法,用函数指针来实现虚函数的功能。
FrancisYoungBaseVirtualtypedef struct _FrancisYoungBaseVirtual
{
// basic info
Metadata * metaInfo;
// fields
int b;
// virtual methods
void (* pPrint4FrancisYoungBaseVirtual)(_FrancisYoungBaseVirtual *); } FrancisYoungBaseVirtual; // type id
static int FrancisYoungBaseVirtualTypeId = 14; // method declarations
void Print4FrancisYoungBaseVirtual(FrancisYoungBaseVirtual * pThis); // the new method
FrancisYoungBaseVirtual * NewFrancisYoungBaseVirtual()
{
// alloc for space
FrancisYoungBaseVirtual * pResult = (FrancisYoungBaseVirtual *)malloc(sizeof(FrancisYoungBaseVirtual)); // initialize basic info
pResult->metaInfo = NewMetadata(
pResult,
FrancisYoungBaseVirtualTypeId,
NULL);
// initialize fields
pResult->b = 12; // initialize virtual methods
pResult->pPrint4FrancisYoungBaseVirtual = Print4FrancisYoungBaseVirtual; // return result
return pResult;
} void Print4FrancisYoungBaseVirtual(FrancisYoungBaseVirtual * pThis)
{
if (pThis == NULL) { return;/* throw exception in C# */ } printf("in base (%d)\n", pThis->b);
}
FrancisYoungDerivedVirtualtypedef struct _FrancisYoungDerivedVirtual
{
// basic info
Metadata * metaInfo; // fields
int d;
// virtual methods } FrancisYoungDerivedVirtual; // type id
static int FrancisYoungDerivedVirtualTypeId = 15; // method declarations
void Print4FrancisYoungDerivedVirtual(FrancisYoungBaseVirtual * pThis); // the new method
FrancisYoungDerivedVirtual * NewFrancisYoungDerivedVirtual()
{
// initialize base class
FrancisYoungBaseVirtual * pBase = NewFrancisYoungBaseVirtual(); // alloc for space
FrancisYoungDerivedVirtual * pResult = (FrancisYoungDerivedVirtual * )malloc(sizeof(FrancisYoungDerivedVirtual));
// initialize basic info
pResult->metaInfo = NewMetadata(
pResult,
FrancisYoungDerivedVirtualTypeId,
pBase->metaInfo); // initialize fields
pResult->d = 13; // initialize virtual methods
pBase->pPrint4FrancisYoungBaseVirtual = Print4FrancisYoungDerivedVirtual;
// return result
return pResult;
} void Print4FrancisYoungDerivedVirtual(FrancisYoungBaseVirtual * pThis)
{
if (pThis == NULL) { return;/* throw exception in C# */ }
FrancisYoungDerivedVirtual * thisObj = (FrancisYoungDerivedVirtual*)Convert2Type(
pThis->metaInfo, FrancisYoungDerivedVirtualTypeId);
if (thisObj == NULL) { return;/* throw exception in C# */ } printf("in derived %d\n", thisObj->d);
}
FrancisYoungManagerVirtualtypedef struct _FrancisYoungManagerVirtual
{
Metadata * metaInfo;
union
{
FrancisYoungBaseVirtual * pBase;
FrancisYoungDerivedVirtual * pDerived;
} obj;
} FrancisYoungManagerVirtual; // type id
static int FrancisYoungManagerVirtualTypeId = 16; // method declarations // the new method
FrancisYoungManagerVirtual * NewFrancisYoungManagerVirtual()
{
// initialize base class // alloc for space
FrancisYoungManagerVirtual * pResult = (FrancisYoungManagerVirtual * )malloc(sizeof(FrancisYoungManagerVirtual));
// initialize basic info
pResult->metaInfo = NewMetadata(
pResult,
FrancisYoungManagerVirtualTypeId,
NULL); // initialize fields
pResult->obj.pBase = NewFrancisYoungBaseVirtual(); // initialize virtual methods // return result
return pResult;
}
4) 使用ManagerVirtual类
C#代码的调用方式如下。
ManagerVirtual m = new ManagerVirtual();
m.derived.Print();// In Base
对应的C代码如下。
FrancisYoungManagerVirtual * m = NewFrancisYoungManagerVirtual();
FrancisYoungBaseVirtual * pBase = (FrancisYoungBaseVirtual*)Convert2Type(m->obj.pDerived->metaInfo, FrancisYoungBaseVirtualTypeId);
pBase->pPrint4FrancisYoungBaseVirtual(pBase);
仍然与之神似。
我们知道,虚方法在C代码中,变成了一个函数指针。根据上一篇文章的分析,我们在这里创建的是父类的对象,所以Print的函数指针指向的是父类的Print方法,即C版代码中的Print4FrancisYoungBaseVirtual方法。所以derived调用的只能是父类的Print4FrancisYoungBaseVirtual方法。
4. 结论
对《颠覆你对方法调用的看法!》的分析结果和原作者是一样的,这也印证了我们上一篇文章的结论的正确性。
感想
上一篇文章我提到,为什么要把C封装为面向对象语言?面向对象语言是如何从无到有的?最开始的那个人是怎么设计出这样一套机制的?他之前没有面向对象的任何概念,他的思路是什么?
通过本文的分析,我的感觉是,面向对象语言的创始人一定是在大量的编写面向过程语言代码的时候,逐渐感受到了现实世界和程序语言的严重脱节,也发现了两者之间可能靠近一些的途径。一个函数写出来,如果几乎不可能被其他人用在其他地方,那应该把它隐藏起来,这就是封装的思想。
而继承的思想是如何产生的,我还是不得而知。
用C表达面向对象语言的机制2——颠覆你对方法调用的看法!的更多相关文章
- 用C表达面向对象语言的机制——C#版
PS:本文PDF版在这里(格式更好看一些).最新的源代码请在本页面文末下载,PDF中的链接不是最新的. 用C表达面向对象语言的机制——C#版 我一直认为,面向对象语言是对面向过程语言的封装.如果是这样 ...
- go 学习笔记之go是不是面向对象语言是否支持面对对象编程?
面向对象编程风格深受广大开发者喜欢,尤其是以 C++, Java 为典型代表的编程语言大行其道,十分流行! 有意思的是这两中语言几乎毫无意外都来源于 C 语言,却不同于 C 的面向过程编程,这种面向对 ...
- C#学习-面向对象语言都有类
面向对象语言的一个基本特征是它们都有类,类是C#(这类语言)中的一种复杂数据类型. 类代表一组具有公共属性和行为的对象. 在C#中定义一个类是非常简单的,只需使用class关键字并按格式来定义即可. ...
- javascript是一种面向对象语言吗?如果是,您在javascript中是如何实现继承的呢
·oop(面向对象程序设计)中最常用到的概念有 1.对象,属性,方法 1>(对象:具体事物或抽象事物,名词) 2>(属性:对象的特征,特点,形容词) 3>(方法:对象的动作,动词) ...
- java反射并不是什么高深技术,面向对象语言都有这个功能,而且功能也很简单,就是利用jvm动态加载时生成的class对象
java反射并不是什么高深技术,面向对象语言都有这个功能. 面向对象语言都有这个功能,而且功能也很简单,就是利用jvm动态加载时生成的class对象,去获取类相关的信息 2.利用java反射可以调用类 ...
- java类加载机制及方法调用
类加载机制 概述 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载(Loading).验证(Verification).准备(Preparation).解析(Resoluti ...
- JVM垃圾回收机制总结:调优方法
转载: JVM垃圾回收机制总结:调优方法 JVM 优化经验总结 JVM 垃圾回收器工作原理及使用实例介绍
- python与java的内存机制不一样;java的方法会进入方法区直到对象消失 方法才会消失;python的方法是对象每次调用都会创建新的对象 内存地址都不i一样
python与java的内存机制不一样;java的方法会进入方法区直到对象消失 方法才会消失;python的方法是对象每次调用都会创建新的对象 内存地址都不i一样
- Android IPC机制(三)在Android Studio中使用AIDL实现跨进程方法调用
在上一篇文章Android IPC机制(二)用Messenger进行进程间通信中我们介绍了使用Messenger来进行进程间通信的方法.可是我们能发现Messenger是以串行的方式来处理client ...
随机推荐
- mvc理解篇
java是面向对象的语言. mvc是一种设计模式,就像论文排版,设置好论文的框架,字体,大小,颜色等,然后把论文内容往里填.mvc的出现让代码的层次更加的清晰,业务通过数据流实现. mvc框架的优点如 ...
- 一般多项式曲线的最小二乘回归(Linear Regression)
对于一般多项式: K为多项式最高项次,a为不确定的常数项,共k+1个; 有离散数据集对应,其方差: β为,方差函数S对β自变量第j个参数的梯度(偏导数): 当以上梯度为零时,S函数值最小,即: 中的每 ...
- Mac 系统下的环境变量
1.查看电脑环境变量 -->echo $PATH 2. 新建环境变量 sudo vim ~/.bash_profile 输入密码 3. 按 I ,编辑新的环境变量地址,保存 退出 :w ...
- Windows下nginx+php配置
1. 首先,将 nginx.conf 中的 PHP 配置注释去掉. # pass the PHP scripts to FastCGI server listening on # #location ...
- 计算机网路之动态NAT配置
配置路由端口的ip地址与打开(省略) 配置路由协议 router eigrp 100 network 211.1.1.0(网络号) 0.0.0.255(通配子掩) network 192.168.1. ...
- css3 自定义动画(1)
<style> /*@-webkit-keyframes 动画名称 {} 用时:-webkit-animation:时间 动画名称; */ /* @-webkit-keyframes mo ...
- 谁动了我的特征?——sklearn特征转换行为全记录
目录 1 为什么要记录特征转换行为?2 有哪些特征转换的方式?3 特征转换的组合4 sklearn源码分析 4.1 一对一映射 4.2 一对多映射 4.3 多对多映射5 实践6 总结7 参考资料 1 ...
- swift的运算符
1.什么是运算符?它有什么作用? 运算符是一种特定的符号或者表达式.它用来验证.修改.合并变量. 2.运算符有哪些? 运算符有很多,很多朋友学的很烦.这里我依据它的作用把它分为几块来介绍: a:赋值运 ...
- MyEclipse 2015 Stable 2.0安装包及破解工具下载
MyEclipse 2015 Stable 2.0安装包及破解文件下载 之前一直在用myeclipse10.7,后来发现10.7版本有点老了,所以就换了2015稳定版的myeclipse,里面附带了破 ...
- Latent semantic analysis note(LSA)
1 LSA Introduction LSA(latent semantic analysis)潜在语义分析,也被称为LSI(latent semantic index),是Scott Deerwes ...