




已阅读5页,还剩18页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
C语言也能面向对象(一)一个简单的类 面向对象不是C+, java, C#等的专利,万能的C语言一样可以面向对象编程。GObject系统已经用C语言实现了一套完整并且功能强大的面向对象系统。但GObject系统十分庞大,并依赖于众多的开发包,在项目本身很小,或者不想依赖于众多于GObject相关的开发包时,就无法使用了。 那么能不能用C语言实现一套小巧简单,易于理解,易于使用的面向对象系统呢?就让我们一起探索一下吧! 所谓类就是数据和方法的集合体。我们使用结构体定义类的数据,如下: 1: struct animal 2: 3: char name256; 4: int weight; 5: ;我们定义了一个animal结构体,成员包括animal的name和animal的weight(重量)。 现在数据已经有了,那么方法如何定义呢? 我们知道在面向对象中有封装的概念,所以结构体中的成员我们不应该直接访问,而应该通过方法访问,我们添加访问结构体成员变量的方法: 1: char* animal_get_name(animal* self); 2: void animal_set_name(animal* self, char* name); 3: int animal_get_weight(animal* self); 4: void animal_set_weight(animal* self, int weight); 5: char* animal_get_name(animal* self) 6: 7: return self-name; 8: 9: 10: void animal_set_name(animal* self, char* name) 11: 12: strncpy(self-name, name, sizeof(self-name); 13: 14: 15: int animal_get_weight(animal* self) 16: 17: return self-weight; 18: 19: 20: void animal_set_weight(animal* self, int weight) 21: 22: self-weight = weight; 23: 24: 方法的第一个参数animal* self是对象的地址,与C+中的this指针是同一个概念,只不过C+中的this指针是由编译器帮我们传递的,无需在代码中显式的写出来。 现在类的数据、方法都有了,那么如何使用类创建、初始化、析构、销毁对象呢? 在C+中有new、delete操作符负责创建和销毁,由构造函数和析构函数负责初始化和析构。这些在我们的C语言面向对象系统中都是要实现的,但对于我们的第一个类,我们可以实现的简单一些,如下: 1: animal* animal_create(void); 2: void animal_destroy(animal* self); 3: 4: animal* animal_create(void) 5: 6: animal* self = malloc(sizeof(animal); 7: LW_ASSERT(self); 8: memset(self-name, 0x00, sizeof(self-name); 9: self-weight = 0; 10: return self; 11: 12: 13: void animal_destroy(animal* self) 14: 15: free(self); 16: 17: animal_create负责对象创建和初始化,animal_destroy负责对象析构和销毁。 到这里我们第一个类的定义就结束了。测试一下吧: 1: animal* animal1 = NULL; 2: animal* animal2 = NULL; 3: 4: animal1 = animal_create(); 5: animal_set_name(animal1, Kitty); 6: animal_set_weight(animal1, 30); 7: printf(animal1, name : %s, weight : %dn, 8: animal_get_name(animal1), 9: animal_get_weight(animal1); 10: animal_destroy(animal1); 11: 12: animal2 = animal_create(); 13: animal_set_name(animal2, Bib); 14: animal_set_weight(animal2, 10); 15: printf(animal2, name : %s, weight : %dn, 16: animal_get_name(animal2), 17: animal_get_weight(animal2); 18: animal_destroy(animal2); 19: 看看测试代码,是不是有些感觉了,其实这里面向对象还远着呢,勉强能称得上是基于对象吧。相关代码可在此处下载下一篇我们将介绍如何通过new和delete操作创建和销毁对象,通过构造函数和析构函数初始化和析构对象。C语言也能面向对象(二)new和delete 上篇我们介绍了如何使用结构体写一个简单的类animal,并使用animal* animal_create(void)和void animal_destroy(animal* self)来创建和删除animal类的对象。在C+中创建和删除对象时使用的是new和delete操作符,并会自动调用类的构造函数和析构函数初始化和析构对象,那么使用C语言如何实现这样的机制呢?我们可以用函数模拟new和delete,定义两个函数:lw_new和lw_delete(“lw”意为light-weight,轻量级的意思)。使用lw_new创建类的对象时,需要为对象申请内存空间,并且需要初始化该对象,所以lw_new函数至少需要知道该类的对象需要多大的内存空间,以及该类的构造函数是谁。我们可以将这些信息封装到一个结构体中,称之为类信息结构体,如下(以上篇介绍的animal类为例):typedef struct _animal_klass_info animal_klass_info;struct _animal_klass_info size_t size; /*animals size*/ animal* (*ctor)(animal* self); /*constructor*/;然后我们创建一个该结构体的对象(称之为类信息),将animal类的内存大小、构造函数等信息存入到对象中,再使用一个全局的指针指向该对象(称之为全局类信息指针),如下:extern animal_klass_info* animal_klass;static animal_klass_info local_animal_klass = sizeof(animal), animal_ctor,;animal_klass_info* animal_klass = &local_animal_klass;这样在创建animal类的对象时,我们只需要将animal类的全局类信息指针animal_klass传入到 lw_new函数中,lw_new函数就能够申请内存空间,并自动调用构造函数初始化对象了,lw_new的实现如下:animal* lw_new(animal_klass_info* klass) animal* p = ANIMAL(malloc(klass-size); return klass-ctor(p);现在可以使用lw_new创建animal类的对象了,如下:animal* animal1 = lw_new(animal_klass);那么如何使用lw_delete函数删除对象,并自动调用析构函数呢?在C+中删除对象时,传递给delete操作符的参数只有对象的地址,所以我们传递给lw_delete函数的也只能是对象的地址。使用对象地址我们可以释放对象占用的内存空间,但如何调用对象的析构函数呢?我们可以将析构函数的地址加入到类信息结构体中,还是以animal类为例,将其类信息结构体修改为:typedef struct _animal_klass_info animal_klass_info;struct _animal_klass_info size_t size; /*animals size*/ animal* (*ctor)(animal* self); /*constructor*/ animal* (*dtor)(animal* self); /*destructor*/;在创建类信息时加入析构函数的地址,如下:static animal_klass_info local_animal_klass = sizeof(animal), animal_ctor, animal_dtor,;然后在animal类中加入一个类信息结构体的指针,用来保存animal类信息的地址。修改animal类定义如下:typedef struct _animal animal;struct _animal/*klass info*/ animal_klass_info* klass; /*private data*/ char name256; int weight;在lw_new函数中初始化animal类的类信息结构体指针,如下:animal* lw_new(animal_klass_info* klass) animal* p = ANIMAL(malloc(klass-size); p-klass = klass; return klass-ctor(p);这样lw_delete函数就能够根据传入的对象的地址,取得相应的类信息地址,进而取得析构函数了,lw_delete实现如下:void lw_delete(animal* self) if(self) free(self-klass-dtor(self); 至此,lw_new和lw_delete都已经实现了,我们测试一下吧.int main(int argc, char* argv) argc; argv; animal* animal1 = NULL; animal* animal2 = NULL; animal1 = lw_new(animal_klass); animal_set_name(animal1, Kitty); animal_set_weight(animal1, 30); printf(animal1, name : %s, weight : %dn, animal_get_name(animal1), animal_get_weight(animal1); lw_delete(animal1); animal2 = lw_new(animal_klass); animal_set_name(animal2, Bib); animal_set_weight(animal2, 10); printf(animal2, name : %s, weight : %dn, animal_get_name(animal2), animal_get_weight(animal2); lw_delete(animal2); return 0;完整的代码可以在此处下载。大家可以看出我们目前实现的lw_new和lw_delete只能用于创建和删除animal类的对象。如何实现通用的lw_new和lw_delete呢?这就是下篇我们将要介绍的内容。C语言也能面向对象(三)通用的new和delete上篇中实现的lw_new和lw_delete函数只能创建和删除animal类的对象,这当然满足不了我们的需要,那么如何实现通用的lw_new和lw_delete函数,用来创建和删除任何类的对象呢?考虑新定义一个类car,其属性有车标logo和颜色color:typedef struct _car car;struct _car/*private data*/ char logo256; int color;成员函数:char* car_get_logo(car* self);void car_set_logo(car* self, char* logo);int car_get_color(car* self);void car_set_color(car* self, int color);使用lw_new创建car类对象时,必须知道car类的对象占用内存空间大小和构造函数,以便为对象申请内存空间,并初始化该对象,而使用lw_delete函数删除car类的对象时,就必须知道car类的析构函数,以便先析构该对象,然后再释放对象占用的内存空间。可以看出,创建和删除car类的对象所需要的三个必备条件就是:类的对象占用内存空间大小,构造函数和析构函数,与创建和删除animal类时所需的条件一致。那么我们是不是可以创建一个统一的类信息结构体,然后使用结构体的不同对象,保存不同类的类信息呢?上篇中定义的animal类的类信息结构体如下:struct _animal_klass_info animal_klass_info* super; /*animals supers klass info*/ char* name; /*animal klasss name*/ size_t size; /*animals size*/ animal* (*ctor)(animal* self); /*constructor*/ animal* (*dtor)(animal* self); /*destructor*/;super为父类的类信息地址,无父类,则其值为NULL,name是该类的名称,这两个成员先不考虑,我们着重考虑size、ctor和dtor这三个成员,看看是否能够将这个animal专用的类信息结构体,改造成统一的类信息结构体。size为类对象所占内存空间大小,类型为size_t,这对任何类都是通用的,所以无需变更。ctor和dtor都是函数指针类型,传入参数为animal对象的地址,在将animal对象初始化或析构后,再将该地址返回。既然要适用于任意类,在类信息结构体中就不能使用animal类的指针了,我们将结构体中这两个函数的参数和返回值类型弱化为void*类型,如下:typedef void* (*voidf)(void*);voidf ctor;voidf dtor;注意修改的只是类信息结构体中的函数类型,在实现animal类的构造函数和析构函数时,仍然可以直接使用animal类的指针类型,这样在初始化类信息时,编译器会警告类型不兼容,为了避免警告,我们将类信息结构体中的构造函数和析构函数指针弱化为void*类型,最终类信息结构体的定义为:typedef struct _klass_info klass_info;struct _klass_info klass_info* super; /*objects klasss super klass*/ char* name; /*objects klasss name*/ size_t size; /*objects size*/ void* ctor; /*objects constructor*/ void* dtor; /*objects destructor*/;lw_new和lw_delete函数的实现也要响应修改,如下:void* lw_new(void* klass) klass_info* kls = KLASS(klass); void* p = malloc(kls-size); *(klass_info*)p) = kls; return (voidf)(kls-ctor)(p); void lw_delete(void* self) if(self) klass_info* kls = *(klass_info*)self); free(voidf)(kls-dtor)(self); 为了避免警告,lw_new和lw_delete函数的传入参数类型也弱化为void*类型。klass_info* kls = KLASS(klass)中使用了KLASS()宏,定义如下:#define KLASS(_klass_info_) (klass_info*)_klass_info_)用来将其他指针强制转换为klass_info*类型。我们以car类为例,看看如何使用klass_info类信息结构体定义类。car.hextern klass_info* car_klass; #define CAR(_object_) (car*)_object_) typedef struct _car car;struct _car/*class info*/ klass_info* klass; /*private data*/ char logo256; int color; char* car_get_logo(car* self);void car_set_logo(car* self, char* logo);int car_get_color(car* self);void car_set_color(car* self, int color);car.c#include car.h static car* car_ctor(car* self);static car* car_dtor(car* self); static klass_info local_car_klass = NULL, car_klass, sizeof(car), car_ctor, car_dtor,;klass_info* car_klass = &local_car_klass; static car* car_ctor(car* self) memset(self-logo, 0x00, sizeof(self-logo); self-color = 0; static car* car_dtor(car* self) return self; char* car_get_logo(car* self) return self-logo; void car_set_logo(car* self, char* logo) strncpy(self-logo, logo, sizeof(self-logo); int car_get_color(car* self) return self-color; void car_set_color(car* self, int color) self-color = color;下面测试一下:int main(int argc, char* argv) argc; argv; animal* animal1 = NULL; animal* animal2 = NULL; car* car1 = NULL; car* car2 = NULL; animal1 = lw_new(animal_klass); animal_set_name(animal1, Kitty); animal_set_weight(animal1, 30); printf(animal1, name : %s, weight : %dn, animal_get_name(animal1), animal_get_weight(animal1); lw_delete(animal1); animal2 = lw_new(animal_klass); animal_set_name(animal2, Bib); animal_set_weight(animal2, 10); printf(animal2, name : %s, weight : %dn, animal_get_name(animal2), animal_get_weight(animal2); lw_delete(animal2); car1 = lw_new(car_klass); car_set_logo(car1, Honda); car_set_color(car1, 0); printf(car1, logo : %s, color : %dn, car_get_logo(car1), car_get_color(car1); lw_delete(car1); car2 = lw_new(car_klass); car_set_logo(car2, BMW); car_set_color(car2, 1); printf(car2, logo : %s, color : %dn, car_get_logo(car2), car_get_color(car2); lw_delete(car2); return 0;到这里我们已经能够使用lw_new和lw_delete函数创建和删除任何类的对象,并且自动调用其构造函数和析构函数了,本篇文章相关代码可以到这里下载。下篇文章我们将讨论继承。C语言也能面向对象(四)(继承) 在C+中如果一个类有父类,那么这个类的对象中就包含了父类中定义的数据,并且可以使用父类的函数访问或操作该这些数据。在C中如何实现这样的机制呢?animal类的定义如下:typedef struct _animal animal;struct _animal/*class info*/ klass_info* klass; /*private data*/ char name256; int weight;现在我们再定义一个dog类,除了包含animal类的属性外,还包括一个age属性,即年龄。如下:typedef struct _dog dog ;struct _dog /*class info*/ klass_info* klass; /*private data*/ char name256; int weight; int age;在保持类的对象的内存布局不变的情况下,我们可以将dog类的定义变换为:typedef struct _dog dog;struct _dog/*base*/ animal base; /*private data*/ int age;两个dog类的定义在内存布局上是完全一致的。因此,只要将dog类对象的指针强制转型为animal类的指针,那么animal类的成员函数就可以访问或操作 dog类对象了,如下:dog* my_dog = lw_new(dog_klass);animal_set_weight(ANIMAL(my_dog), 40);int weight = animal_get_weight(ANIMAL(my_dog);lw_delete(my_dog);在C+中创建子类的对象时,要先调用父类的构造函数,然后再调用子类的函数;删除子类对象时,要先调用子类的析构函数,然后再调用父类的析构函数。animal类是dog类的父类,在定义dog类信息时,要将dog类信息中的super属性初始化为animal类信息的地址,如下:static klass_info local_dog_klass = animal_klass, dog_klass, sizeof(dog), dog_ctor, dog_dtor,;在实现dog的构造函数时,就可以使用类信息的super成员获取其父类的构造函数地址,如下:static dog* dog_ctor(dog* self) (voidf)(klass_of(self)-super-ctor)(self); self-age = 0; return self;klass_of函数的功能是获取对象的类信息地址。可以看出dog类的构造函数是先调用父类的构造函数,然后再初始化子类的属性。类似,析构函数的实现如下:static dog* dog_dtor(dog* self) (voidf)(klass_of(self)-super-dtor)(self); return self;目前为止,我们都是使用结构体初始化的方式来初始化类信息,这种方式有代码重复、容易犯错,难于维护的缺点,例如,animal类信息的初始化在animal.c中已经实现过,定义dog类时在dog.c中又要再次实现,如果再定义其他继承自animal类的子类,则还要实现。按顺序初始化结构体这种做法本身就很容易犯错,如果这样的代码到处都是,那么维护难度就可想而知了。所以我们考虑使用类信息初始化函数来初始化类信息,即专门定义一个函数用来初始化类信息,该函数在程序运行后,第一次创建该类的对象时由lw_new函数调用。在类信息结构体中新加入一个属性,即类信息初始化函数的地址,如下:typedef struct _klass_info klass_info;struct _klass_info void* init; /*initialize function*/ klass_info* super; /*objects klasss super klass*/ char* name; /*objects klasss name*/ size_t size; /*objects size*/ void* ctor; /*objects constructor*/ void* dtor; /*objects destructor*/;init属性在定义静态类信息结构体对象时初始化,其他属性在init指向的函数中初始化,以animal类为例:static animal_klass_info local_animal_klass = animal_init;animal_klass_info* animal_klass = &local_animal_klass; void animal_init(void) if(animal_klass-init) animal_klass-init = NULL; animal_klass-super = NULL; animal_klass-name = animal_klass; animal_klass-size = sizeof(animal); animal_klass-ctor = animal_ctor; animal_klass-dtor = animal_dtor; lw_new函数修改如下:void* lw_new(void* klass) klass_info* kls = KLASS(klass); if(kls-init) (init_fun)kls-init)(); void* p = malloc(kls-size); *(klass_info*)p) = kls; return (voidf)(kls-ctor)(p);类信息初始化函数中会将类信息结构体的init属性置空,所以该函数只会被调用一次,即第一次创建该类的对象时调用。子类的类信息初始化函数可以直接调用父类的类信息初始化函数,以dog类为例:void dog_init(void) if(dog_klass-init) animal_init(); memcpy(dog_klass, animal_klass, sizeof(animal_klass_info); dog_klass-super = animal_klass; dog_klass-name = dog_klass; dog_klass-size = sizeof(dog); dog_klass-ctor = dog_ctor; 至此我们实现了类继承,并使用类信息初始化函数来初始化类信息。这里是相关代码。下篇文章我们将讨论多态。C语言中的面向对象(1)类模拟和多态,继续在面向对象的语言里面,出现了类的概念。这是编程思想的一种进化。所谓类:是对特定数据的特定操作的集合体。所以说类包含了两个范畴:数据和操作。而视频教程c语言中的struct仅仅是数据的集合。()1实例:下面先从一个小例子看起#ifndef C_Class #define C_Class struct#endif C_Class A C_Class A *A_this; void (*Foo)(C_Class A *A_this); int a; int b; C_Class B /B继续了A C_Class B *B_this; /顺序很重要 void (*Foo)(C_Class B *Bthis); /虚函数 int a; int b; int c; void B_F2(C_Class B *Bthis) printf(It is B_Funn); void A_Foo(C_Class A *Athis) printf(It is A.a=%dn,Athis-a);/或者这里/ exit(1);/ printf(纯虚 不答应执行n);/或者这里 void B_Foo(C_Class B *Bthis) printf(It is B.c=%dn,Bthis-c); void A_Creat(struct A* p) p-Foo=A_Foo; p-a=1; p-b=2; p-A_this=p; void B_Creat(struct B* p) p-Foo=B_Foo; p-a=11; p-b=12; p-c=13; p-B_this=p; int main(int argc, char* argv) C_Class A *ma,a; C_Class B *mb,b; A_Creat(&a);/实例化 B_Creat(&b); mb=&b; ma=&a; ma=(C_Class A*)mb;/引入多态指针 printf(%dn,ma-a);/可惜的就是 函数变量没有private ma-Foo(ma);/多态 a.Foo(&a);/不是多态了 B_F2(&b);/成员函数,因为效率问题不使用函数指针 return 0;输出结果:11It is B.c=13It is A.a=1It is B_Fun 本文章来自 21视频教程网 C语言中的面向对象(2)C语言的多态实现相信很多人都看过设计模式方面的书,大家有什么体会呢?Bridge,Proxy,Factory这些设计模式都是基于抽象类的。使用抽象对象是这里的一个核心。 其实我觉得框架化编程的一个核心问题是抽象,用抽象的对象构建程序的主体框架,这是面向对象编程的普遍思想。用抽象构建骨架,再加上多态就形成了一个完整的程序。由于C语言本身实现了继续和多态,使用这样的编程理念(理念啥意思?跟个风,嘿嘿)在C中是十分普遍的现象,可以说Virtual(多态)是VC的灵魂。 但是,使用视频教程c语言的我们都快把这个多态忘光光了。我常听见前辈说,类?多态?我们用的是C,把这些忘了吧。很不幸的是,我是一个固执的人。这么好的东西,为啥不用呢。很兴奋的,在最近的一些纯C代码中,我看见了C中的多态!下面且听我慢慢道来。 1. VC中的Interface是什么Interface:中文解释是接口,其实它表示的是一个纯虚类。不过我所要说的是,在VC中的Interface其实就是struct,查找Interface的定义,你可以发现有这样的宏定义: #Ifndef Interface #define Interface struct #endif而且,实际上在VC中,假如一个类有Virtual的函数,则类里面会有vtable,它实际上是一个虚函数列表。实际上C是从C发展而来的,它不过是在语言级别上支持了很多新功能,在C语言中,我们也可以使用这样的功能,前提是我们不得不自己实现。 2C中如何实现纯虚类(我称它为纯虚结构)比较前面,相信大家已经豁然开朗了。使用struct组合函数指针就可以实现纯虚类。例子: typedef struct void (*Foo1)(); char (*Foo2)(); char* (*Foo3)(char* st); MyVirtualInterface; 这样假设我们在主体框架中要使用桥模式。(我们的主类是DoMyAct,接口具体实现类是Act1,Act2)下面我将依次介绍这些“类”。(C中的“类”在前面有说明,这里换了一个,是使用早期的数组的办法) 主类DoMyAct: 主类中含有MyVirtualInterface* m_pInterface; 主类有下函数: DoMyAct_SetInterface(MyVirtualInterface* pInterface) m_pInterface= pInterface; DoMyAct_Do() if(m_pInterface=NULL) return; m_pInterface-Foo1(); c=m_pInterface-Foo2(); 子类Act1:实现虚结构,含有MyVirtualInterface stMAX; 有以下函数: MyVirtualInterface* Act1_CreatInterface() index=FindValid() /对象池或者使用Malloc !应该留在外面申请,实例化 if(index=-1) return NULL; Stindex.Foo1=Act1_Foo1; / Act1_Foo1要在下面具体实现 Stindex.Foo2=Act1_Foo2; Stindex.Foo3=Act1_Foo3; Return &st index; 子类Act2同上。 在main中,假设有一个对象List。List中存贮的是MyVirtualInterface指针,则有: if( (p= Act1_CreatInterface() != NULL) List_AddObject(&List, p
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年精神科常见心理疾病诊疗试题答案及解析
- 物业安全生产月活动方案范本
- 2025年消化内科临床实习考核试卷答案及解析
- 2025-2030耳鼻喉显微镜品牌国际化战略与跨文化营销挑战报告
- 2025-2030纺织服装行业发展趋势与政策支持研究报告
- 2025-2030番茄汁包装材料创新与可持续发展解决方案报告
- 反洗钱合规性调研报告范文
- 2025-2030根际促生菌剂在节水农业中的应用场景拓展报告
- 2025年医学心理学医患心理疏导与支持答案及解析
- 2025-2030智慧城市基础设施建设投资热点与政策导向分析报告
- 浙教版2025-2026学年八年级上科学第1章 对环境的察觉 单元测试卷
- 基底细胞癌护理查房
- 2025保密观知识竞赛题库(试题附答案25个)
- 2025-2026学年人教版(2024)初中生物八年级上册(全册)教学设计(附目录)
- 煤矿监管培训方案
- 企业反腐倡廉培训课件
- 湿疮湿疹中医护理查房
- 2025年6月新《中华人民共和国治安管理处罚法》全文+修订宣贯解读课件(原创内容丰富且全)
- DB31/T 1377.4-2022实验鸡和鸭第4部分:设施及环境
- 2025邮储银行面试题目及答案
- 他人借车免责协议书
评论
0/150
提交评论