硬核,用C语言实现面向对象!!!
用C语言实现面向对象,听起来就有不可思议的感觉,那真的可以实现吗?我觉得OK!
要使用C语言这种面向过程的语言来实现面向对象,首先要做的事情是去了解面向对象是怎样的概念,例如:继承的本质是怎样的一回事,多态又是怎么来的。
在面向对象语言中,继承通过子类声明为某个父类的继承,在C++中,继承的方式有public 继承,private 继承,protected 继承,分别为公有继承,私有继承,保护继承,但是值得注意的是,在往后的高级语言中,比如说Java语言,C#语言,都仅提供公有继承,为什么呢?因为私有继承和保护继承带来的复杂性远大于实用性。
特立独行的多态又是何方神圣?(独白 : 多态多态,一种代码,多种状态)要掌握多态,首先得知道,编译原理里难以理解的的静态连编,动态连编。
简单来说,静态连编就是编译器在编译期间就可以确定该调用的对象是什么,而动态连编刚好相反。
C++如何实现多态?通过virtual关键字定义虚函数,而虚函数又有纯虚函数!在这,纯虚函数的内容就一笔带过,略微提及了,纯虚函数用于实现抽象类。
class Parent
{protected:int mi;int mj;
public:virtual void print(){cout << " mi = " << mi << " mj = " << mj << endl;}
};class Child : public Parent
{private:int mk;
public:Child(int x,int y,int z){mi = x;mj = y;mk = z;}virtual void print(){cout << " mi = " << mi << " mj = " << mj << " mk = " << mk << endl;}
};
如果要在子类中重写父类中的成员函数,则必须使用虚函数声明,否则重写是没有意义的。子类中如果重写了父类中的同名函数,会发生同名覆盖,在子类中直接访问的就是子类的成员函数,(如果想调用父类的同名函数,解决办法是利用作用域分辨符来调用父类的成员函数)。
学习是循序渐进的,掌握了虚函数,多态的真正意义就开始浮现了。
void print_your_name(Parent* p)
{p->print();
}Parent p;
Child c;
print_your_name(&p);
print_your_name(&c);
阅读至此,我想应该来个little episode,赋值兼容,不得不说,C++的强大是天生!子类对象可以用来初始化父类对象,子类对象可以赋值给父类对象,父类对象指针又可指向子类对象,父类引用又可代替子类对象,所以上图所写的函数参数其实可以输入子类对象,因为什么?赋值兼容。赋值兼容,赋值兼容。为什么要强调三次,因为在设计模式里,这样的写法是随处存在。题外话了,继承和多态都是设计模式里的中流砥柱,至关重要。
纸上谈兵,毫无益处。现在就用C语言实现面向对象,启航吧!
//code.h#ifndef _CODE_H_
#define _CODE_H_
typedef void Class; // 作为中间值,既可输出变量地址,又可接收参数地址
typedef void Derived; // 作为子类Class* Class_Demo_Create(int i,int j);
int Class_Demo_getI(Class* pthis); // 手工传递地址
int Class_Demo_getJ(Class* pthis);
int Class_Demo_Add(Class* pthis,int value);
void Class_Demo_Free(Class* pthis);Derived* Derived_Demo_Create(int i,int j,int k);
int Derived_Demo_getK(Derived* pthis);
int Derived_Demo_Add(Derived* pthis,int value);#endif
待我自圆其说吧,为什么要把Class定义为void的呢,因为C语言里没有private关键字,那如何实现呢,同样的,要知其然,必先知其所以然。声明为私有的属性即成员变量,不能被外界直接访问,只能通过成员函数访问,当我们使用void接收参数时,可以为任意的指针类型,当我们使用void作为返回值,也可以返回任意类型的指针。But 要使用所创建的对象,必须要进行强制类型转换。
实现继承和多态,有两个关键点是,继承其实在内存中是直接复制了父类的成员变量,多态使用的虚函数生成的时候编译器自动给每个对象添加虚函数表,顾名思义,就是用来存放虚函数的表,在C++中,这是一种数据结构,我们用结构体来代替就可以了,通过虚函数表找到对象要调用的真正函数。
// code.c#include <malloc.h>
#include "code.h"static int Virtual_Class_Demo_Add(Class* pthis,int value);
static int Virtual_Derived_Demo_Add(Derived* pthis,int value);struct Vtble // 虚函数表
{int (*padd)(Derived*,int); // 函数指针
};struct Class_Demo
{struct Vtble* mtble; // 指向虚函数表的指针int mi;int mj;
};struct Derived_Demo
{struct Class_Demo d; // 模拟继承于父类int mk;
};static struct Vtble Class_Demo_vtble = // 父类的虚函数表
{Virtual_Class_Demo_Add
};static struct Vtble Derived_Demo_vtble = // 子类的虚函数表
{ Virtual_Derived_Demo_Add
};Class* Class_Demo_Create(int i,int j) // 返回void*类型,模拟私有成员,使之不能在外界直接访问
{struct Class_Demo* ret = (structClass_Demo*)malloc(sizeof(struct Class_Demo));if (NULL != ret){ret->mtble = &Class_Demo_vtble; // 关联虚函数表ret->mi = i;ret->mj = j;}return ret;
}int Class_Demo_getI(Class* pthis)
{struct Class_Demo* obj = (struct Class_Demo*)pthis; // 模拟成员函数,调用私有成员return obj->mi;
}int Class_Demo_getJ(Class* pthis)
{struct Class_Demo* obj = (struct Class_Demo*)pthis;return obj->mj;
}int Class_Demo_Add(Class* pthis,int value)
{struct Class_Demo* obj = (struct Class_Demo*)pthis;return obj->mtble->padd(pthis,value); // 实现多态,关键所在
}static int Virtual_Class_Demo_Add(Class* pthis,int value) // 真正的实现函数
{struct Class_Demo* obj = (struct Class_Demo*)pthis;return obj->mi + obj->mj + value;
}Derived* Derived_Demo_Create(int i,int j,int k)
{struct Derived_Demo* ret = (struct Derived_Demo*)malloc(sizeof(struct Derived_Demo));if (NULL != ret){ret->d.mtble = &Derived_Demo_vtble;ret->d.mi = i; // 模拟继承父类中的变量ret->d.mj = j;ret->mk = k;}return ret;
}int Derived_Demo_getK(Derived* pthis)
{struct Derived_Demo* obj = (struct Derived_Demo*)pthis; return obj->mk;
}static int Virtual_Derived_Demo_Add(Derived* pthis,int value)
{struct Derived_Demo* ret = (struct Derived_Demo*)(pthis);return ret->mk + value;
}int Derived_Demo_Add(Derived* pthis,int value)
{struct Derived_Demo* ret = (struct Derived_Demo*)(pthis);return ret->d.mtble->padd(pthis,value);
}void Class_Demo_Free(Class* pthis)
{free(pthis); // 释放地址,可以为任意类型,亦可用来释放子类对象
}
大神Linus说,talk is cheap,show your code.代码如上,但是单单看代码,神仙也会被绕晕 。 所以要实现用C语言写面向对象,必须深入理解c++中的面向对象的实现原理,这时肯定有异声传来,那为什么不是其他高级语言呢?因为其他高级语言是以c++为基础的,掌握c++ 中的面向对象领悟了,把整个过程理解清楚,使用什么语言,都可以实现面向对象。
// main.c#include <stdio.h>
#include "code.h"void run(Class* pthis,int v) // 展现多态的函数
{int r = Class_Demo_Add(pthis,v);printf("result = %d\n",r);
}int main(int argc, char const *argv[])
{Class* c = Class_Demo_Create(1,1);Derived* d = Derived_Demo_Create(2,2,2);printf("mk = %d\n",Derived_Demo_getK(d));printf("Class_Demo_Add = %d\n",Class_Demo_Add(c,3));printf("Derived_Demo_Add = %d\n",Derived_Demo_Add(d,3));run(c,3);run(d,3);Class_Demo_Free(c);Class_Demo_Free(d);return 0;
}
输出结果嘛,上机试一下就知道了。
硬核,用C语言实现面向对象!!!相关推荐
- 硬核!C语言八大排序算法,附动图和详细代码解释!
来源 :C语言与程序设计.竹雨听闲等 一 前言 如果说各种编程语言是程序员的招式,那么数据结构和算法就相当于程序员的内功. 想写出精炼.优秀的代码,不通过不断的锤炼,是很难做到的. 二 八大排序算法 ...
- 硬核知识,C/C++移植法则分享
随着移动互联网和物联网技术的兴起,网络加密性要求以及HTTPS 流量不断提升,x86架构越发难以满足企业的需求,地位变得越来越不稳固. 近年来,在ARM优势急速凸显后,x86应用向ARM的迁移也成为一 ...
- 10W+字C语言硬核总结(二),值得阅读收藏!
0.为什么使用指针 假如我们定义了 char a='A' ,当需要使用 'A' 时,除了直接调用变量 a ,还可以定义 char *p=&a ,调用 a 的地址,即指向 a 的指针 p ,变量 ...
- 电竞c语言,“电竞专业”考题曝光,硬核的你能满分吗?
原标题:"电竞专业"考题曝光,硬核的你能满分吗? 大家知道近两年,由于电竞行业的蓬勃发展,以及国家对电竞的逐步认可,其商业市场也是越来越大,随之而来的就是电竞方面的人才极度稀缺,许 ...
- php foreach 循环 判断index 小于多少_PHP设计模式之迭代器模式 - 硬核项目经理
一说到这个模式,就不得不提循环语句.在<大话设计模式>中,作者说道这个模式现在的学习意义更大于实际意义,这是为什么呢?当然就是被foreach这货给整得.任何语言都有这种类似的语法可以方便 ...
- 李笑来登GitHub趋势榜第一,教你自学编程,含37%“硬核鸡汤”
郭一璞 乾明 发自 凹非寺 量子位 报道 | 公众号 QbitAI 从杭州买房指南,到女装大佬集合,GitHub从来不缺神奇的项目. 这不,又一个项目,跃迁到了GitHub趋势榜的第一名,所以,今天 ...
- 硬核干货Java集合详解
目录 一.问题是最好的老师 二.集合的由来 三.数组存在的问题 四.数组和集合的区别? 五.集合是什么? 六.集合整体架构图 七.集合架构图详解 1.Collection 2.List ArrayLi ...
- 当了12年大学教师,跟大家聊聊嵌入式工程师,硬核单片机编程思想
当了12年大学教师,跟大家聊聊嵌入式工程师,硬核单片机编程思想 摘要:没有思想的裸程序就如一副人体骨架,有个人形,但没有人样,骨骼之间的关节都是靠胶水或拉线连接起来的,生硬而呆板.假如给骨架包上皮肉, ...
- 超硬核!苏州同程旅游学长给我的全面的面试知识库
超硬核!苏州同程旅游学长给我的全面的面试知识库 1.简介 新生和经验丰富的C#面试常见问题解答 2.什么是C#? 3.用示例说明C#中的注释类型 4.可以执行多个catch块吗? 5. public, ...
最新文章
- Error: cannot allocate vector of size XX Gb
- 任意角度人脸检测pcn
- linux引数列项目过长,Linux 命令个人总结====== 未完待续 个人认为比较重要
- 将用户数据分成一个个数据块传输的优点不包括( )
- python如何删除代码_Python如何删除除字母和数字之外的所有字符?(代码示例)
- [POJ 3709] K-Anonymous Sequence(斜率优化dp / 动态维护凸包)
- 理解 JavaScript 闭包{转载}
- 猫哥教你写爬虫 004--数据类型转换-小练习
- 远程桌面连接-GPU加速
- HP DV3 笔记本 重装系统
- 使用fseek()函数随机访问文件
- 为什么谐振时电抗为0_变频谐振耐压试验装置在进行电缆耐压试验原理
- 数据流图、数据字典的画法
- matlab如何插入“埃”这个符号
- OrCAD多页原理图器件按页编号的设置
- html5均线图源码,通达信导航家之成本均线主图指标 源码(图文)
- Android ToolBar修改返回按钮图标
- vue前端开发微信支付和支付宝支付
- CSV文件乱码问题解决
- 根据出生日期判断星座
热门文章
- 使用D3.js实现框选节点并进行多节点拖动
- 古溪镇为民工作室 ‖“让青春飞扬”成长小组之“校园欺凌和暴力预防”
- IPA第八届少儿模特明星盛典 濮阳赛区 海选赛圆满落幕
- kingbase V7/V8大小写敏感问题
- 一个人的旅游 hdu2066(多起点多终点 弗洛伊德算法)
- 算法之暴力美学的开始
- 如何实现用户派单用户抢单
- TrueNAS SCALE 做底层,虚拟机安装黑群晖7.1.1的方法
- 2021年,Flutter 与 React Native该如何选择?
- 键盘调节台式计算机声音,不能正常使用键盘快捷键来调整声音。怎么办?