1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 如何用c语言读取硬盘串号_如何用C语言实现OOP

如何用c语言读取硬盘串号_如何用C语言实现OOP

时间:2024-03-31 11:52:17

相关推荐

如何用c语言读取硬盘串号_如何用C语言实现OOP

我们知道面向对象的三大特性分别是:封装、继承、多态。很多语言例如:C++和Java等都是面向对象的编程语言,而我们通常说C是面向过程的语言,那么是否可以用C实现简单的面向对象呢?答案是肯定的!C有一种数据结构叫做结构体(struct)和函数指针,使用结构体和函数指针便可实现面向对象的三大特性。

C语言实现封装

首先我们先简单了解一下什么是封装,简单的说封装就是类将属性和属性操作封装在一个不可分割的独立实体,只提供对外访问属性的操作方法。用户无需知道对象的内部实现细节,但能通过对外提供的接口访问内部属性数据。由于C没有像C++一样可以设置类内部数据的访问权限,所以C的属性和操作都是公有的,但是我们可以用C的函数指针模仿C++实现简单的封装。后续的多态实现也用到C的函数指针。我们知道C++所有的非静态成员函数会有一个this指针,通过this指针可以访问所有的成员变量和成员函数。而C可以通过传入成员变量所在的结构体指针,达到C++ this指针的效果。现在我们构建一个简单的Bird类,Bird有名称(Name),颜色(Color),重量(Weight),栖居地(Addr)属性和对应的操作方法。

enum{ INVALID_COLOR = 0, RED = 1, GREEN = 2,};struct Bird{ char *Name; char *Addr; int Color; int Weight; void (*SetName)(struct Bird *Bird, char *Name); void (*SetAddr)(struct Bird *Bird, char *Addr); void (*SetColor)(struct Bird *Bird, const int Color); void (*SetWeight)(struct Bird *Bird, const int Weight); char *(*GetName)(struct Bird *Bird); int (*GetColor)(struct Bird *Bird);};

代码中SetName, SetAddr, SetColor, SetWeight函数指针相当于C++类的成员函数,是Bird类内部数据与外部交互的接口。在C++中this指针是在编译的时候由编译器自己加上去的,所以每个接口都有一个struct Bird* 类型形参,该指针的作用相当于C++的this指针,通过该指针可以访问类内部的所有成员变量和成员函数。接下来就需要实现具体的函数,再在执行构造函数时手动将函数指针指向最终的实现函数。

具体成员函数实现源码如下:

void SetBirdName(struct Bird *Bird, const char * const Name){ if(Bird == NULL){ return; } Bird->Name = Name;}void SetBirdAddr(struct Bird *Bird, const char * const Addr){ if(Bird == NULL){ return; } Bird->Addr = Addr;}void SetBirdColor(struct Bird *Bird, const int Color){ if(Bird == NULL){ return; } Bird->Color = Color;}void SetBirdWeight(struct Bird *Bird, const int Weight){ if(Bird == NULL){ return; } Bird->Weight = Weight;}char *GetName(struct Bird *Bird){ if(Bird == NULL){ return NULL; } return Bird->Name;}int GetColor(struct Bird *Bird){ if(Bird == NULL){ return INVALID_COLOR; } return Bird->Color;}

那么C++的构造函数和析构函数如何使用C来实现呢?构造函数在创建一个对象实例时自动调用,析构函数则在销毁对象实例时自动调用,实际上C++的构造函数和析构函数在编译期间由编译器插入到源码中。但是编译C源码时,编译器没有这种操作,需要我们手动去调用构造函数和析构函数。而且在调用C的构造函数时,需要我们手动将函数指针指向最终的实现函数。在调用C的析构函数时,需要我们手动地释放资源。

构造函数源码如下:

void BirdInit(struct Bird *Bird){ if(Bird == NULL){ return; } Bird->SetAddr = SetBirdAddr; Bird->SetColor = SetBirdColor; Bird->SetName = SetBirdName; Bird->SetWeight = SetBirdWeight; Bird->GetColor = GetColor; Bird->GetName = GetName; Bird->SetAddr(Bird, "Guangzhou"); Bird->SetColor(Bird, RED); Bird->SetWeight(Bird, 10); Bird->SetName(Bird, "Xiaoming");}

析构函数源码如下:

void BirdDeinit(struct Bird *Bird){ if(Bird == NULL){ return; } memset(Bird, 0, sizeof(struct Bird));}

至此,C如何实现面向对象的封装特性已讲完,下面看看我们实际运用的效果。

int main(int argc, char *argv[]){ struct Bird *Bird = (struct Bird *)malloc(sizeof(struct Bird)); BirdInit(Bird); //调用构造函数 Bird->SetName(Bird, "Lihua"); //更改Bird的名称 Bird->SetColor(Bird, GREEN); //更改Bird的颜色 printf("Bird name: %s, color: %d", Bird->GetName(Bird), Bird->GetColor(Bird)); BirdDeinit(Bird); //调用析构函数 free(Bird); Bird = NULL;}

在mac上编译执行结果如下:

C语言实现封装运行结果图

C语言实现继承

我们继续简单了解一下什么是继承,继承就是使用已存在的类的定义基础建立新类的技术。新类可以增加新的数据和方法,但不能选择性地继承父类。而且继承是“is a”的关系,比如老鹰是鸟,但是你不能说鸟就是老鹰,因为还有其他鸟类动物也是鸟。因为C语言本身的限制,只能用C实现C++的公有继承(除非使用C开发新的计算机语言)。在C++使用公有继承(没有虚函数),编译器会在编译期间将父类的成员变量插入到子类中,通常是按照顺序插入(具体视编译器决定)。说到这里,我们很容易就能想到如何使用C语言实现C++的公有继承了(不带虚函数),就是在子类中定义一个父类的成员变量,而且父类的成员变量只能放在最开始的位置。依旧使用上面建立的Bird类作为父类,我们建立一个新的子类Eagle(老鹰),老鹰可以飞翔也吃肉(其他鸟类不一定会飞和吃肉),所以我们建立的子类如下:

struct Eagle{ struct Bird Bird; BOOL Fly; BOOL EateMeat; void (*CanFly)(struct Bird *Bird, const BOOL Fly); void (*CanEateMeat)(struct Bird *Bird, const BOOL EateMeat); BOOL (*IsFly)(struct Bird *Bird); BOOL (*IsEateMeat)(struct Bird *Bird);};extern void EagleInit(struct Eagle *Eagle);extern void EagleDeinit(struct Eagle *Eagle);

在C++中new一个子类对象,构造函数的调用顺序则是从继承链的最顶端到最底端,依次调用构造函数。而delete一个子类对象时,析构函数的调用顺序则是从继承链的最底端到最顶端依次调用。按照这个模式,我们子类(Eagle)的构造函数和析构函数就很容易写了,构造函数和析构函数源码如下所示:

void EagleInit(struct Eagle *Eagle){ if(Eagle == NULL){ return; } BirdInit(&Eagle->Bird); Eagle->CanFly = CanFly; Eagle->CanEateMeat = CanEateMeat; Eagle->IsFly = IsFly; Eagle->IsEateMeat = IsEateMeat; Eagle->CanFly((struct Bird *)Eagle, TRUE); Eagle->CanEateMeat((struct Bird *)Eagle, TRUE);}void EagleDeinit(struct Eagle *Eagle){ if(Eagle == NULL){ return; } memset(Eagle, 0, sizeof(struct Eagle)); BirdDeinit(&Eagle->Bird);}

在子类的构造函数EagleInit中先调用父类的构造函数BirdInit,在子类的析构函数中先释放子类的资源再调用父类的析构函数BirdDeinit。至此,我们完成了C语言实现C++的公有继承(不带虚函数)。

C语言实现多态

所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。

老惯例,我们来看一下C++是如何实现运行时多态的。C++的运行时多态是用虚函数实现的。在C++中有虚函数的类存在一个虚函数表指针vptr指向一个虚函数表。而虚函数表则存放着,虚函数对应的实现函数。我们用C语言实现类似于C++的多态性,可以模仿C++用创建虚函数表和在类中定义一个虚函数表指针实现。但是我们一般不用这样实现,因为这种实现方式有几个缺点:

1、添加和删除一个虚函数时,虚函数表大小要随着改变,函数在虚函数表里面存放的位置也要随着改变。

2、会增加类的内存占用空间。

3、多层间接访问虚函数,增加了运行开销和系统复杂度。

通过仔细观察C语言实现继承我们可以知道,父类的成员变量会全部放入到子类内存空间中。那么我们是否可以把虚函数表直接放在类中呢?这个时候函数指针又发挥作用了!我们可以把多个函数指针放在父类中,就可以在之类构造函数中直接将父类里的函数指针重新指向新的实现函数,这就实现了我们想要的多态性!因为鸟类都会下蛋,所以我们定义一个下蛋的函数LayEggs。

Bird类源码如下:

struct Bird{ char *Name; char *Addr; int Color; int Weight; void (*SetName)(struct Bird *Bird, char *Name); void (*SetAddr)(struct Bird *Bird, char *Addr); void (*SetColor)(struct Bird *Bird, const int Color); void (*SetWeight)(struct Bird *Bird, const int Weight); char *(*GetName)(struct Bird *Bird); int (*GetColor)(struct Bird *Bird); void (*LayEggs)(struct Bird *Bird);};extern void BirdInit(struct Bird *Bird);extern void BirdDeinit(struct Bird *Bird);

Bird类构造函数源码如下:

static void LayEggs(struct Bird *Bird){ if(Bird == NULL){ return; } printf("bird lay eggs");}void BirdInit(struct Bird *Bird){ if(Bird == NULL){ return; } Bird->SetAddr = SetBirdAddr; Bird->SetColor = SetBirdColor; Bird->SetName = SetBirdName; Bird->SetWeight = SetBirdWeight; Bird->GetColor = GetColor; Bird->GetName = GetName; Bird->LayEggs = LayEggs; Bird->SetAddr(Bird, "Guangzhou"); Bird->SetColor(Bird, RED); Bird->SetWeight(Bird, 10); Bird->SetName(Bird, "Xiaoming");}

Eagle类构造函数源码如下:

static void LayEggs(struct Bird *Bird){ if(Bird == NULL){ return; } printf("Eagle lay eggs");}void EagleInit(struct Eagle *Eagle){ if(Eagle == NULL){ return; } BirdInit(&Eagle->Bird); Eagle->CanFly = CanFly; Eagle->CanEateMeat = CanEateMeat; Eagle->IsFly = IsFly; Eagle->IsEateMeat = IsEateMeat; Eagle->Bird.LayEggs = LayEggs; Eagle->CanFly((struct Bird *)Eagle, TRUE); Eagle->CanEateMeat((struct Bird *)Eagle, TRUE);}

​在Eagle构造函数中,我们将父类的函数指针指向了新的LayEggs函数,在程序运行期间就会调用新的LayEggs函数。我们修改main函数,观察运行结果。

main函数修改如下:

int main(int argc, char *argv[]){ struct Bird *Bird = (struct Bird *)malloc(sizeof(struct Bird)); BirdInit(Bird); //调用构造函数 Bird->SetName(Bird, "Lihua"); //更改Bird的名称 Bird->SetColor(Bird, GREEN); //更改Bird的颜色 printf("Bird name: %s, color: %d", Bird->GetName(Bird), Bird->GetColor(Bird)); Bird->LayEggs(Bird); BirdDeinit(Bird); //调用析构函数 free(Bird); Bird = NULL; Bird = (struct Bird *)malloc(sizeof(struct Eagle)); struct Eagle *Eagle = (struct Eagle *)Bird; EagleInit((struct Eagle *)Bird); Bird->SetName(Bird, "Tanmeimei"); Bird->SetAddr(Bird, "Shanghai"); Bird->SetColor(Bird, RED); printf("Eagle is fly: %d, is eate meat: %d", Eagle->IsFly((struct Bird *)Eagle), Eagle->IsEateMeat((struct Bird *)Eagle)); printf("Eagle name is: %s,", Bird->GetName(Bird)); Bird->LayEggs(Bird); EagleDeinit((struct Eagle *)Bird); free(Bird); Bird = NULL; return 0;}

运行结果如下:

C语言实现OOP运行结果图

到目前为止,我们已经用C语言实现了封装、继承和多态三大面向对象特性!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。