C语言实现C++面向对象语言多态特性

时间:2021-04-24 19:52:16

        C++语言演化过程中,加入了不少新特性,使其成为一门现代高级OO语言。当初C++在上个世纪七十年代末,80年代初时,C++发展刚刚起步,那时C++可以看作C with class,C++编写的代码是经过转换成C语言代码后,再经过C编译器来生成目标代码的。如今,C++语言已经自成一套体系,有自己语言书写风格和设计理念。C++整个语法体系中包括了C++基础(兼容C语言语法特性,OO特性)、模板库&STL和泛型编程等知识。由于C++学习时间成本高(目前可以看编程语言排行榜就知道,这门语言流行度在下降,正被其他语言追赶,比如最近流行的python与javascript),一般人要熟练掌握其大部分特性都得3~5年时间。其实现在大部分品牌公司目前停留在了C with Class阶段,简单点说就是用C++这门语言来作产品类封装和搭建框架,其他特性也是不推荐使用的,更别提C++11和C++17后新增加的特性了。


      这里我简单表明下自己对C++语言的看法:个人学习成本与时间成本在如今项目开发中是不得不考虑的问题,在现有计算机体系结构下,CPU执行效率与内存空间大小一般情况下不是开发的瓶颈(某些嵌入式领域除外)。若开发项目时,要在运行效率与开发时间长短上作选择的话,一般公司肯定以开发时间效率高的语言作为首选。C++走的是另外一条统一路线,想一门语言做成所有的事情,结果是语言越来越臃肿,运行效率方面比不上C语言,开发效率又跟不上如今的java,python等语言。前段时间,公司内网上有个博客牛人写了篇博客,深入探讨了C++与C之间的联系与区别,C++语言内部模块耦合太强,也就是说,即使你没有用这个特性,C++在链接的时候,由于语言模块内部依赖性问题也会编译到目标代码中去。一个简单的hello world程序,C++目标文件大小会比C语言大十几K。目前,一般成熟的产品,如果考虑效率高的,一般就直接用C语言来写了,比如nginx,redis,libevent等。再比如类Unix操作系统(linux)大部分代码也是由C语言写的。目前流行的那几种语言,就是因为学习成本低,入手快而被广大程序员所接受。当然就我目前所了解的,不少游戏公司驱动引擎还是由C++写的,某些大型嵌入式产品也会考虑用C++来做。同时目前市场上也有不错的第三方C++库,比如boost,QT,ACE等库,我曾经一段时间用QT写过不少客户端程序来支撑公司测试和维护某一类路由器产品,尤其QT中引入QtQuick特性后,开发时间上也提升了不少,帮助公司节省了25~30%的时间成本。


       下面回到正题,C++语言基本语法中包含了面向对象的三大特性:封装,继承,多态。正是因为C++在语法层面上支持了这些面向对象特性,才使得大型项目会考虑用此语言来搭建平台或产品框架。其实也可以这样说,面向对象是相对于面向过程而言的,更多地是一种思维方式,世间万物皆对象,感觉跟毕达哥拉斯学派宣称那样:万物皆数。所以面向对象作为一种软件设计方式(当然面向对象还有独特的SOLID设计原则),其他语言完全可以借用,比如说C语言就可以。我们在linux内核代码中就可以经常看到面向对象的特性。比如文件操作中将属性与方法(一般定义为函数指针)封装到特定结构体中。我们知道,C++与C一样,是一门静态语言,当代码编译后,其函数地址就相对固定了,那是怎么通过运行时来改变函数行为的呢? C++教科书中有介绍说,多态是通过虚函数实现的。如果在基类某些函数前面声明为virtual,则C++编译器会在内存对象模型前面插入一个虚函数指针,该指针指向一个虚函数表,正是因为我们可以改变虚函数指针所指函数表地址,从而实现其多态操作。下面我们以一个简单的示例来用C实现C++的继承与多态特性。


/*
*
*C语言实现C++面向对象多态特性
* Created on: 2015年12月26日
* Author: @CodingGeek
*/
#include <stdio.h>
#include <stdlib.h>

struct base;

struct base_vtbl
{
void(*dance)(struct base *);
void(*jump)(struct base*, int high);
};

struct base
{
struct base_vtbl *vptr;
/*base members */
};

void base_dance(struct base *pb)
{
pb->vptr->dance(pb);
}

void base_jump(struct base *pb, int high)
{
pb->vptr->jump(pb, high);
}

void based_dance(struct base *pb)
{
printf("base dance\n");
}

void based_jump(struct base *pb, int high)
{
printf("base jump:%d\n", high);
}
/* global vtable for base */
struct base_vtbl base_table =
{
based_dance,
based_jump
};

void base_init(struct base *pb)
{
pb->vptr = &base_table;
}

struct derived1
{
struct base super;
/*derived members */
};

void derived1_dance(struct derived1 *pd)
{
/*implementation of derived1's dance function */
printf("derived1 dance\n");
}

void derived1_jump(struct derived1 *pd, int high)
{
/*implementation of derived1's jump function */
printf("derived1 jump:%d\n", high);
}

/*global vtable for derived1 */
struct base_vtbl derived1_table =
{
(void(*)(struct base *))&derived1_dance,
(void(*)(struct base*, int))&derived1_jump
};

void derived1_init(struct derived1 *pd)
{
pd->super.vptr = &derived1_table;
/*init base members d->super.foo */
/*init derived1 members d->foo */
}

struct derived2
{
struct base super;
/*derived2 members */
};

void derived2_dance(struct derived2 *pd)
{
/*implementation of derived2's dance function*/
printf("derived2 dance\n");
}

void derived2_jump(struct derived2 *pd, int high)
{
/*implementation of derived2's jump function */
printf("derived2 jump:%d\n", high);
}

/*global vtable for derived2 */
struct base_vtbl derived2_table =
{
(void(*)(struct base *))&derived2_dance,
(void(*)(struct base*, int))&derived2_jump
};
void derived2_init(struct derived2 *pd)
{
pd->super.vptr = &derived2_table;
/*init base members d->super.foo */
/*init derived1 members d->foo */
}


int main(void)
{
/*OK~! We're done with our declarations, now we can finally do
* some polymorphism in C
*/
/*这里类似C++等面向对象语言的构造函数,初始化内存模型,包括了虚函数指针初始化等*/
struct base b;
base_init(&b);
struct base *b_ptr = (struct base*)&b;
base_dance(b_ptr);
base_jump(b_ptr, 99);

struct derived1 d1;
derived1_init(&d1);

struct derived2 d2;
derived2_init(&d2);

/*这里看作是类型转换,C语言中只有强制转换,或者类型隐式提升,对应于C++的
* reinterpret_cast,当然C++还有static_cast与dynamic_cast等类型转化
*/

struct base *b1_ptr = (struct base *)&d1;
struct base *b2_ptr = (struct base *)&d2;

/*这里实际上间接引用的就是子类的函数地址*/
base_dance(b1_ptr); /*calls derived1_dance */
base_jump(b1_ptr, 88);/*calls derived1_jump */

base_dance(b2_ptr); /*calls derived2_dance */
base_jump(b2_ptr, 77); /*calls derived2_jump */

return 0;
}

从上面运行结果可以看出,C语言实现多态的特性是没有问题的,问题在于我们如何建立抽象模型(实际上类似C++内存对象模型)。而C++恰好原生支持了这一切,不需要我们重复造*了。