用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语言实现面向对象!!!相关推荐

  1. 硬核!C语言八大排序算法,附动图和详细代码解释!

    来源 :C语言与程序设计.竹雨听闲等 一 前言 如果说各种编程语言是程序员的招式,那么数据结构和算法就相当于程序员的内功. 想写出精炼.优秀的代码,不通过不断的锤炼,是很难做到的. 二 八大排序算法 ...

  2. 硬核知识,C/C++移植法则分享

    随着移动互联网和物联网技术的兴起,网络加密性要求以及HTTPS 流量不断提升,x86架构越发难以满足企业的需求,地位变得越来越不稳固. 近年来,在ARM优势急速凸显后,x86应用向ARM的迁移也成为一 ...

  3. 10W+字C语言硬核总结(二),值得阅读收藏!

    0.为什么使用指针 假如我们定义了 char a='A' ,当需要使用 'A' 时,除了直接调用变量 a ,还可以定义 char *p=&a ,调用 a 的地址,即指向 a 的指针 p ,变量 ...

  4. 电竞c语言,“电竞专业”考题曝光,硬核的你能满分吗?

    原标题:"电竞专业"考题曝光,硬核的你能满分吗? 大家知道近两年,由于电竞行业的蓬勃发展,以及国家对电竞的逐步认可,其商业市场也是越来越大,随之而来的就是电竞方面的人才极度稀缺,许 ...

  5. php foreach 循环 判断index 小于多少_PHP设计模式之迭代器模式 - 硬核项目经理

    一说到这个模式,就不得不提循环语句.在<大话设计模式>中,作者说道这个模式现在的学习意义更大于实际意义,这是为什么呢?当然就是被foreach这货给整得.任何语言都有这种类似的语法可以方便 ...

  6. 李笑来登GitHub趋势榜第一,教你自学编程,含37%“硬核鸡汤”

    郭一璞 乾明 发自 凹非寺  量子位 报道 | 公众号 QbitAI 从杭州买房指南,到女装大佬集合,GitHub从来不缺神奇的项目. 这不,又一个项目,跃迁到了GitHub趋势榜的第一名,所以,今天 ...

  7. 硬核干货Java集合详解

    目录 一.问题是最好的老师 二.集合的由来 三.数组存在的问题 四.数组和集合的区别? 五.集合是什么? 六.集合整体架构图 七.集合架构图详解 1.Collection 2.List ArrayLi ...

  8. 当了12年大学教师,跟大家聊聊嵌入式工程师,硬核单片机编程思想

    当了12年大学教师,跟大家聊聊嵌入式工程师,硬核单片机编程思想 摘要:没有思想的裸程序就如一副人体骨架,有个人形,但没有人样,骨骼之间的关节都是靠胶水或拉线连接起来的,生硬而呆板.假如给骨架包上皮肉, ...

  9. 超硬核!苏州同程旅游学长给我的全面的面试知识库

    超硬核!苏州同程旅游学长给我的全面的面试知识库 1.简介 新生和经验丰富的C#面试常见问题解答 2.什么是C#? 3.用示例说明C#中的注释类型 4.可以执行多个catch块吗? 5. public, ...

最新文章

  1. Error: cannot allocate vector of size XX Gb
  2. 任意角度人脸检测pcn
  3. linux引数列项目过长,Linux 命令个人总结====== 未完待续 个人认为比较重要
  4. 将用户数据分成一个个数据块传输的优点不包括( )
  5. python如何删除代码_Python如何删除除字母和数字之外的所有字符?(代码示例)
  6. [POJ 3709] K-Anonymous Sequence(斜率优化dp / 动态维护凸包)
  7. 理解 JavaScript 闭包{转载}
  8. 猫哥教你写爬虫 004--数据类型转换-小练习
  9. 远程桌面连接-GPU加速
  10. HP DV3 笔记本 重装系统
  11. 使用fseek()函数随机访问文件
  12. 为什么谐振时电抗为0_变频谐振耐压试验装置在进行电缆耐压试验原理
  13. 数据流图、数据字典的画法
  14. matlab如何插入“埃”这个符号
  15. OrCAD多页原理图器件按页编号的设置
  16. html5均线图源码,通达信导航家之成本均线主图指标 源码(图文)
  17. Android ToolBar修改返回按钮图标
  18. vue前端开发微信支付和支付宝支付
  19. CSV文件乱码问题解决
  20. 根据出生日期判断星座

热门文章

  1. 使用D3.js实现框选节点并进行多节点拖动
  2. 古溪镇为民工作室 ‖“让青春飞扬”成长小组之“校园欺凌和暴力预防”
  3. IPA第八届少儿模特明星盛典 濮阳赛区 海选赛圆满落幕
  4. kingbase V7/V8大小写敏感问题
  5. 一个人的旅游 hdu2066(多起点多终点 弗洛伊德算法)
  6. 算法之暴力美学的开始
  7. 如何实现用户派单用户抢单
  8. TrueNAS SCALE 做底层,虚拟机安装黑群晖7.1.1的方法
  9. 2021年,Flutter 与 React Native该如何选择?
  10. 键盘调节台式计算机声音,不能正常使用键盘快捷键来调整声音。怎么办?