C语言学习笔记—封装

0、简述

        我们知道每个对象都有一组属性和一组附加在其上的功能,将这些属性和功能放入名为对象的实体中,这个过程叫做封装。

        封装过程首先在我们的头脑中发生,然后被转变为代码,当我们觉得一个对象需要一些属性和功能时,就会在脑海中进行封装,然后,将封装转变到代码级别。对象由一组属性和一组功能组成,不论是属性还是功能都应该封装到对象胶囊中,我们先看一下其中的属性封装。

1、属性封装

        先知道两个概念,隐式封装 和 显示封装。

        隐式封装:其意思是程序员是唯一知道对象存在的人,编程语言对它们一无所知。例如用变量名将变量分组在p1和p2,编程语言只看到10个看似相互独立的变量,这是一个低级的封装,以至于它不被正式认为是封装。通过变量名实现封装在所有编程语言中都存在(因为可以命名变量)。即使在汇编语言中也是如此。

        如例1:

        显示封装:显示的意思是程序员和编程语言都知道封装和对象的存在。幸运的是C语言提供了显示属性封装,这也是我们能够狠容易地用它编写这么多本质上面向对象的程序的原因之一。但C语言不提供显示的行为封装(方法封装),我们不得不采用隐式规则来支持这一点。

        在C语言中,结构提供属性封装,如我们利用结构重写上述的代码:

        当我们将x,y,red,green,blue属性加入到新类型pixel_t时,就进行了属性封装。

        在代码执行时,p1和p2是显示对象。它们两个都是pixel_t类型,并且它们只有由结构规定的属性。在C语言中,特别是C++中,类型规定了对象的属性

        新类型pixel_t仅是类(或对象模板)的属性。而“类”这个词指的是同时包含属性和功能的对象模板。因为C结构只有属性,所以它不能与类相对应。在C语言中,属性和功能是分开的,我们在代码中隐式地将它们联系到一起。每个类对C都是隐式的,它指的是一个单个的结构和一系列C函数。

        可以发现,我们正在基于模板构造对象,模板需要预先确定的属性,对象在诞生时就应该具有该属性。C语言中结构只存储属性而不存储功能。

        当声明一个对象后,两个事情几乎同时发生:首先是为对象分配内存,然后用默认的值初始化属性。在C语言和其他很多语言中,使用(.)来访问对象中的属性,或者使用(->)来通过存储在指针中的地址间接访问结构中的属性。

        属性当然不是唯一可以封装到对象中的东西,紧接下来看一看行为封装。

2、行为封装

        对象只是属性和方法的胶囊。“方法”是另一个标准术语,通常用来表示保存在对象中的一段逻辑或功能。它可以被看作是一个具有名字、参数、和返回类型的C函数。属性传达的是值,而方法传达的是行为。因此,一个对象有一系列的值,也可以在系统中执行某些行为。

        拓展:在C++中,方法通常被叫作成员函数,而属性被叫做数据成员。

        如果观察一下一些有名的开源C项目,为了封装某些内容而使用的技术,可以发现有一些共同规则。这个技术,在这里可以称为隐式封装。它是隐式的,因为它并没有提供一个C语言能够了解的显示行为封装方法。基于目前从ANSI C标准中所了解的内容,C语言不可能处理有关类操作。因此,在C语言中实现面向对象的技术都不得不是隐式的。

        隐式封装技术有如下建议:

        1、使用C结构来为对象设置属性(显示属性封装)。这种结构称为属性结构。

        2、使用C函数实现行为封装,这些函数称为行为函数。我们知道,不能在C语言结构中实现函数,因此这些函数必须位于属性结构的外面(隐式行为封装)。

        3、行为函数必须采用 结构指针 作为参数。指针指向对象的属性结构。这是因为行为函数可能需要读取或修改对象的属性。

        4、行为函数必须具有合适的命名,以表明它们都和同一类的对象相关。我们需要坚持两个规则,这是其中之一,其目的是为了有清晰的封装。另一个是在属性结构的名称中使用 _t 后缀。

        5、通常,对行为函的声明语句与属性结构的声明语句都放在同一个头文件中。

        6、行为函数的定义通常放在一个或多个单独的源文件中,这些文件需要包含声明头文件。

       注意,使用隐式封装,类确实存在,但它们是隐式的,只有程序员指定。下面的例子展示了如何在C语言中使用这种技术。此例是一个汽车对象,它能够加速直到耗尽燃料时停止。

        例2.1,下面的头文件包含了新类型car_t的声明,car_t 是Car类的属性结构。头文件中还包含Car类的行为函数所需的声明。在这里用“Car类”来指代的是C代码中缺少的隐式类,它集合了属性结构和行为函数:

        关于隐式封装技术,有一点非常重要:每个对象都有自己的独特的属性结构变量。但所有对象都共享相同的行为函数。也就是说,我们必须从属性结构类型为每个对象创建一个专用变量。但我们只编写一次行为函数,并为不同的对象调用它们。

        注意:car_t 属性结构本身不是一个类,它只包含了Car类的属性。这些声明一起构成隐式Car类。

        下面的源文件包含了上述示例2.1中的行为函数定义:

#include<stdio.h>

#include<string.h>

#include<stdlib.h>

#include"car.h"


void car_construct(car_t* car, const char* name)

{

    strcpy(car->name, name);

    car->speed = 0.0;

    car->fuel  = 0.0;

    return;

}


void car_destruct(car_t* car)

{

    return;

}


void car_accelerate(car_t* car)

{

    car->speed += 0.05;

    car->fuel -= 1.0;

    if(car->fuel < 0.0)

    {

        car->speed = 0.0;

    }

    return;

}


void car_brake(car_t* car)

{

    car->speed -= 0.07;

    if(car->speed < 0.0)

    {

        car->speed = 0.0;

    }



    car->fuel -= 2.0;

    if(car->fuel < 0.0)

    {

        car->fuel = 0.0;

    }

    return;

}


void car_refuel(car_t* car, float amount)

{

    car->fuel = amount;

    return;

}

        可以看到,所有的函数都将car_t指针作为其第一个参数。这样允许函数读取和修改对象的属性。如果一个函数不具备指向属性结构的指针,那么它可以被认为是一个普通的C函数,它不代表对象的行为。

        注意:行为函数的声明要位于相应属性结构的声明旁边。这是因为程序员是唯一负责维护属性结构和行为函数对应关系的人,而且维护应该足够便捷,因此将两个集合保持在一起是很有必要的。

        下面中的示例,包含main函数,并执行主逻辑,所有的行为函数都被使用了:

        在C语言中,我们不能把属性和行为函数捆绑在一起。相反,我们必须使用文件来对它们进行分组。但在C++中,有一个语法可以完成捆绑,就是类定义。它允许将属性和行为函数放在一起。

        注意:C和C++在对象析构方面有一个重要的区别。在C++中,当在栈顶部给对象分配空间时,与其他变量一样,只要超出栈的范围,析构函数就会被自动调用。

  • 23
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值