打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
C语言面向对象编程(一):封装与继承

    最近在用 C 做项目,之前用惯了 C++ ,转回头来用C 还真有点不适应。 C++ 语言中自带面向对象支持,如封装、继承、多态等面向对象的基本特征。 C 原本是面向过程的语言,自身没有内建这些特性,但我们还是可以利用 C 语言本身已有的特性来实现面向对象的一些基本特征。接下来我们就一一来细说封装、继承、多态、纯虚类等面向对象特性在 C 语言中如何实现,并且给出实例。

    这篇文章中我们先说封装和继承。

    先来看封装。

    所谓封装,通俗地说,就是一个姑娘化了妆,只给你看她想让你看的那一面,至于里面是否刮了骨、垫了东西,不给你看。说到封装就得说隐藏,这是对兄弟概念;其实我理解隐藏是更深的封装,完全不给你看见,而封装可能是犹抱琵琶半遮面。封装在 C++ 语言中有 protected 、 private 关键字在语言层面上支持,而 C 语言中没有这些。 C 有结构体( struct ),其实可以实现封装和隐藏。

    在 QT 中,为了更好的隐藏一个类的具体实现,一般是一个公开头文件、一个私有头文件,私有头文件中定义实现的内部细节,公开头文件中定义开放给客户程序员的接口和公共数据。看看 QObject (qobject.h ),对应有一个 QObjectPrivate (qobject_p.h ) ,其他的也类似。而代码框架如下:

  1. QObject{  
  2. public:  
  3.     xxx  
  4.     xxx  
  5. private:  
  6.     QObjectPrivate * priv;  
  7. };  
    我们在 C 语言中完全可以用同样的方法来实现封装和隐藏,只不过是放在结构体中而已。代码框架如下:

  1. struct st_abc_private;  
  2. struct st_abc {  
  3.     int a;  
  4.     xxx;  
  5.     void (*xyz_func)(struct st_abc*);  
  6.   
  7.     struct st_abc_private * priv;  
  8. };  
    上面的代码,我们只前向声明结构体 struct st_abc_private ,没人知道它里面具体是什么东西。假如 struct st_abc 对应的头文件是 abc.h ,那么把 st_abc_private 的声明放在 abc_p.h 中,abc.c 文件包含 abc_p.h ,那么在实现 struct st_abc 的函数指针 xyz_func 时如何使用 struct st_abc_private ,客户程序员根本无须知道。

    这样做的好处是显而易见的,除了预定义好的接口,客户程序员完全不需要知道实现细节,即便实现经过重构完全重来,客户程序员也不需要关注,甚至相应的模块连重新编译都不要——因为 abc.h 自始至终都没变过。

    上面代码有个问题,客户程序员如何得到 struct st_abc 的一个实例,他不知道 struct st_abc_private 如何实现的呀。 C 中没有构造函数,只好我们自己提供了:我们可以在 abc.h 中声明一个类似构造函数的函数来生成 struct st_abc 的实例,名字就叫作 new_abc() ,函数原型如下:

  1. struct st_abc * new_abc();  
    至于实现,我们放在 abc.c 中,客户程序员不需要知道。相应的,还有个类似析构函数的函数,原型如下:

  1. void delete_abc(struct st_abc *);  

    到现在为止,封装和隐藏就实现了,而且很彻底。接下来看继承。

    什么是继承?在面向对象层面上不讲了,只说语法层面。语法层面上讲,继承就是派生类拥有父类的数据、方法,又添了点自己的东西,所谓子承父业,发扬光大。在 C 语言中可以用结构体的包含来实现继承关系。代码框架如下:

  1. struct st_base{  
  2.     xxx;  
  3. };  
  4.   
  5. struct st_derived{  
  6.     struct sb_base base;  
  7.     yyy;  
  8. };  
    代码上就是这么简单,不过有一点要注意:第一点就是派生类(结构体)中一定要把父类类型的成员放在第一个。

    继承在语法层面上看,有数据成员、函数,数据成员通过上面的方法自动就“继承”了,至于函数,在结构体表示为函数指针,其实也是一个数据成员,是个指针而已,也会自动“继承”。之所以还要在这里列出来说明,是因为 C++ 中有一个很重要的概念:重载。要在 C 中完整实现有点儿麻烦。

    重载,我们常说的重载大概有三种含义:

  • 其一,函数重载,指函数名字一样,参数个数、类型不一样的函数声明和实现。由于 C 编译器的缘故,不支持。不过这个影响不大。
  • 其二,重定义或者说覆盖,指派生类中定义与基类签名一样(名字、返回值、参数完全一样)的非虚函数,这样派生类的中的函数会覆盖基类的同签名函数,通过成员操作符访问时无法访问基类的同签名函数。
  • 其三,虚函数重写,指在派生类中实现基类定义的虚函数或纯虚函数。虚函数是实现多态的关键,可以在结构体中使用函数指针来表达,但要完全实现,也很麻烦。

    我们平常在交流时通常不明确区分上面三种类型的重载,这里出于习惯,也不作区分。
    好了,第一篇就到这里,有时间会往下续。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
多态和C++多态的实现(汇总)
浅谈封装,继承,多态,绑定
dotNET面试题汇总系列连载(2):面向对象
多态的实现、重载和覆盖(重写)
operator==.cpp
利用C语言结构体封装函数
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服