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

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

    这篇文章中咱们先说封装和继承。框架

    先来看封装。函数

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

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

[cpp] view plain copy指针

  1. QObject{  orm

  2. public:  对象

  3.     xxx  blog

  4.     xxx  继承

  5. private:  

  6.     QObjectPrivate * priv;  

  7. };  

    咱们在 C 语言中彻底能够用一样的方法来实现封装和隐藏,只不过是放在结构体中而已。代码框架以下:

[cpp] view plain copy

  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() ,函数原型以下:

[cpp] view plain copy

  1. struct st_abc * new_abc();  

    至于实现,咱们放在 abc.c 中,客户程序员不须要知道。相应的,还有个相似析构函数的函数,原型以下:

[cpp] view plain copy

  1. void delete_abc(struct st_abc *);  


    到如今为止,封装和隐藏就实现了,并且很完全。接下来看继承。

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

[cpp] view plain copy

  1. struct st_base{  

  2.     xxx;  

  3. };  

  4.   

  5. struct st_derived{  

  6.     struct sb_base base;  

  7.     yyy;  

  8. };  

    代码上就是这么简单,不过有一点要注意:第一点就是派生类(结构体)中必定要把父类类型的成员放在第一个。

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

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

  • 其一,函数重载,指函数名字同样,参数个数、类型不同的函数声明和实现。因为 C 编译器的缘故,不支持。不过这个影响不大。

  • 其二,重定义或者说覆盖,指派生类中定义与基类签名同样(名字、返回值、参数彻底同样)的非虚函数,这样派生类的中的函数会覆盖基类的同签名函数,经过成员操做符访问时没法访问基类的同签名函数。

  • 其三,虚函数重写,指在派生类中实现基类定义的虚函数或纯虚函数。虚函数是实现多态的关键,能够在结构体中使用函数指针来表达,但要彻底实现,也很麻烦。

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

网友评论:

其二,重定义或者说覆盖,指派生类中定义与基类签名同样(名字、返回值、参数彻底同样)的非虚函数,这样派生类的中的函数会覆盖基类的同签名函数,经过成员操做符访问时没法访问基类的同签名函数。
我以为,覆盖不须要签名同样,只要函数名相同就能实现覆盖。

相关文章
相关标签/搜索