• C语言是C++的母语,万变不离指针,指针是C语言的一大法宝!


    我们都知道C语言是一门过程性语言,所谓过程性就是在解决问题时,将问题按步骤分解。

    例如,做菜的时候,先点火,再倒油,接着下菜翻炒,最后加盐和酱油。但有时候借鉴面向对象的思想来组织代码,逻辑层次会更加清晰。

    C和C++的最大区别便是,C++有类,C没有类的概念。单单这一个类使得C缺失很多的东西。好在C有结构体,勉强可以当0.1个类来使用。

    众所周知,类有三大特性:封装、继承、多态。我们来看看C语言如何借鉴类的三大特性来更好的组织代码。

    1、继承

    C语言没有严格意义上的继承,可以借助结构体嵌套实现类似于继承的形式,但始终不尽人意。


     

    C++的类可以实现成员的访问控制,例如将变量b声明成private,那么外部就无法访问。但C的结构体做不到。

    在C++里头,父亲的私有成员,儿子是无法访问的。结构体嵌套也做不到。因为结构体根本就没有访问控制的概念。

    对于C++而言,访问控制实质上是在编译层做的,我们仍旧可以通过指针来间接访问。

    例如:


     

    尽管b被声明成私有,但我们仍旧有办法访问它(借助指针绕过语法检查):


     

    2、封装

    封装就是把数据和方法打包到一个类里面。C++的实现大致如下:


     

    这样做的好处是显而易见的。

    一个类实现了一个小模块,使得代码结构比较清晰。对外接口和数据定义成public,允许调用者直接访问。

    内部接口和数据定义成private,外部不可见。

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


     

    我们可以借助C语言的指针和结构体来实现方法和数据的封装。基本框架如下:


     

    在结构体里定义成员变量很容易,直接int a;

    在结构体里定义成员函数要使用函数指针,比如:


     

    所以,我们把上面的框架具体化就是:


     

    实际上,C++的成员函数也是通过函数指针的形式来实现,本质上是一致的。

    我们都知道类的成员函数和类的成员变量是分开存储的,同一个类的所有对象,成员函数只需要占据一份地址空间。

    在定义结构体之后,函数指针并没有赋值,一般我们会定义一个结构体初始化函数来初始化结构体成员,这有点类似于类的构造函数,但类的构造函数在创建对象时自动调用,而我们这个结构体初始化函数只能自己手动调用了。

    同样的,对标C++的析构函数,我们在C语言里头有一个去初始化的函数来完成模块的去初始化,这种思想不就是一样的吗?


     

    伪构造函数

    注意,我们把两个operation函数定义成了static,这样子文件之外的函数就不能调用它,只能通过manager结构体来调用。是不是感觉有点封装的意味。


     

    去初始化函数我就不写了。

    为了达到上面的目的,简单修改下,我们把函数operation2定义成一种类型,


     

    结构体定义稍作修改:


     

    结构体初始化函数也要做相应的修改,增加了一个函数指针形参:


     

    通过上面的操作,我们用结构体和函数指针完成了模块化封装。

    我看了网上的博客,有些人为了特意模仿类,还用以下方式实现了类似于类的构造函数:


     

    以及类似于类的析构函数:


     

    使用示例:


     

    个人不是很喜欢这种做法,万一忘记调用manager_delete还有内存泄露的风险。

    结构体归根到底还是结构体,不能实现成员对外不可见。而C++中将成员声明成private之后,外部就无法访问了。

    C语言里想这么做,只能将该成员移出结构体,定义为static形式。因为C不支持在结构体内部定义static变量(不信,你可以自己去试下)。

    为何不能在结构体内定义static变量,想想就知道了,static变量的地址在编译链接之后是唯一且确定的,而结构体只有在实例化时才能确定其地址,并且每个结构体实例都有自己的地址空间。

    3、多态

    多态在上面的例子也有体现。C语言实现的多态并非是严格意义上的多态,但是这种思想的应用很广泛,我们姑且叫它多态吧。你不解C++的多态也没关系,丝毫不影响你理解下文。

    linux的VFS便借鉴了这种思想。VFS(Virtual File System)是内核提供的文件系统抽象层,其提供了文件系统的操作接口,可以隐藏底层不同文件系统的实现。

    一个文件系统无非就是实现对文件、目录的管理。针对文件VFS定义了统一的结构体:


     

    strcut file代表一个文件,每种文件系统(比如ext3,vfat)实现读写等操作的方式都不一样,所以将这些方法封装成函数指针,统一定义在结构体struct file_operations内。


     

    每个文件系统各自完成自己的实现。

    再写一个实际的例子。

    定义一个人的标准接口和数据如下:


     

    中国人见面时,说你好:


     

    英国人见面时,说hello:


     

    现在来初始化它们各自的问候方式:


     

    英国人和中国人对外呈现都是struct man,其见面问候的接口都是man.say_hello,但其底层实现却可以不一样。

    并且我们可以在程序运行时,随意的更改中国人的问候方式。比如婴儿时期,只会“哇哇”叫,长大了才会说“你好”,我们可以改变成员say_hello的值,让其在不同时期指向不同的函数,从而达到运行时多态的目的。

    其实呢,C++的多态,也是通过函数指针来实现的,学习过C++的同学就会知道,含有虚函数的类,会维护一个虚函数表,里面存放了虚函数的地址。

    所以说啊,C语言是C++的母语,万变不离指针,指针是C语言的一大法宝。


     

    最后,不管你是转行也好,初学也罢,进阶也可,如果你想学编程~

    ——【值得关注】我的 C/C++编程学习交流俱乐部!——

    涉及:C语言、C++、windows编程、网络编程、QT图形界面开发、Linux编程、游戏编程、数据结构与算以及数据库......

  • 相关阅读:
    SkyWalking链路追踪系统-告警篇
    在k8s中解决pod资源的正确识别
    SkyWalking链路追踪系统-接入篇
    Jenkins API+Pipeline深度实践之input的自动化
    SkyWalking链路追踪系统-部署篇
    DevOps建设之基于钉钉OA审批流的自动化上线
    使用kube-prometheus部署k8s监控(最新版)
    基于k8s手动部署rabbitmq集群
    ant desgin vue中table复选框根据状态disabled置灰
    ant design vue 中tree实现单选
  • 原文地址:https://www.cnblogs.com/huya-edu/p/14178467.html
Copyright © 2020-2023  润新知