使用C语言实现面向对象编程
2021/6/30 1:21:13
本文主要是介绍使用C语言实现面向对象编程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
使用C语言实现面向对象编程
使用C语言实现面向对象编程 – tommwq.tech/blog
面向对象是一种程序设计方法。面向对象不是某种语法或语言特性,因此使用任何高级语言都可以实现面向对象程序设计。与之相似的,使用面向对象程序语言,也可以做出非面向对象的程序设计。这里简单介绍一种用C语言实现面向对象的方法。
面向对象的核心原则是使用对象来组织程序。对象是可以执行某些行为的东西。为了保证行为是正确的,对象需要维护控制行为的一组状态。要避免状态被外部代码破坏,对象必须保护这些状态,这就产生了面向对象的第一个特性:封装。
在C语言中,结构体可以将一组相关的状态(变量)保存在一起。这是否就是封装呢?答案是否定的。结构体只是状态的简单聚合,无法起到保护状态的作用。封装的目的在于保护状态的一致性,必须禁止外部代码直接修改状态。因此我们需要使用结构体和函数相结合的方法来实现封装。这里要注意,为了避免客户代码直接修改结构体,需要将结构体定义保存在私有的.c文件中。头文件保留前向声明,在函数中使用结构体指针。下面看一个例子。
Listing 1: persion.h
struct Person; struct Person* Person_create(); void Person_destroy(struct Person*); int Person_set_age(struct Person *person, unsigned int age); int Person_set_name(struct Person *person, const char *name);
Listing 2: person.c
#define MAX_NAME_LENGTH 64 #define MAX_AGE 120 struct Person { char name[MAX_NAME_LENGTH]; unsigned int age; }; int Person_set_age(struct Person *person, unsigned int age) { if (age > MAX_AGE) { return -1; } person->age = age; return 0; } int Person_set_name(struct Person *person, const char *name) { if (strlen(name) > MAX_NAME_LENGTH) { return -1; } strncpy(person->name, name); return 0; }
上面的代码实现了一个简单的业务模型:一个人有名字和年龄。这个模型有两个约束:
- 名字长度不超过MAX_NAME_LENGTH。
- 年龄不超过MAX_AGE。
如果把结构体Person直接暴露给客户代码,我们将无法保证对象状态始终是符合约束的。因为客户代码可能会设置一个错误的状态,比如:
struct Person *person = Person_create(); person->age = MAX_AGE + 100; // 状态约束被客户代码破坏。
而通过隐藏结构体定义和定义操作函数的方法,我们可以在操作函数中对状态的转移进行控制,保证状态始终是满足约束的,是合法的。
面向对象的第二个特性是继承:子类可以继承父类的状态和行为。继承状态可以通过将父类结构体包含在子类中实现。为了让子类继承父类行为,我们将父类的操作保存到一个接口结构体中,通过复制这个接口结构体实现行为的继承。父类需要作为子类的第一个成员,这样通过指针引用时,子类实例和父类实例可以使用同一个指针表示。
typedef void (*Student_print_name)(struct Student *); typedef void (*Student_set_name)(struct Student *, const char *); typedef void (*Student_print_score)(struct Student *s); typedef void (*Student_set_score)(struct Student *s, int score); typedef int (*Student_get_score)(struct Student *s); typedef int (*Student_compare_score)(struct Student *lhs, struct Student *rhs); typedef void (*Student_destroy)(struct Student *s); struct StudentInterface { Student_print_name print_name; Student_set_name set_name; Student_print_score print_score; Student_set_score set_score; Student_get_score get_score; Student_compare_score compare_score; Student_destroy destroy; }; struct StudentInterface _student_interface; struct Object { void* interface; }; struct Student { struct Object object; char name[16]; int score; }; struct CheatStudent { struct Student base; int cheated; }; struct CheatStudentInterface _cheatstudent_interface; void CheatStudent_class_init() { // 通过复制父类接口列表,实现行为继承。 memcpy(&_cheatstudent_interface.base, _student_interface, sizeof(_student_interface)); } struct CheatStudent* CheatStudent_create() { struct CheatStudent *s = (struct CheatStudent*) malloc(sizeof(struct CheatStudent)); if (s == NULL) { return NULL; } s->base.object.interface = &_cheatstudent_interface; s->cheated = 0; return s; }
面向对象的另一个特性是多态。为了实现多态,我们需要根据实例的具体类型进行函数分派。我们的做法是将函数分派表保存到类实例中。每个类拥有自己的函数表。在初始化类的时候设置这个函数表。同一个类的实例共享这个函数表。每个实例中都包含一个指针指向类函数表。函数表也使用结构体表示。父类函数表是子类的函数表的第一个成员。在初始化子类(不是初始化子类实例)时,将父类的函数表复制到子类函数表起始的位置。然后初始化子类的特有函数。在分派函数时,根据实例内的指针找到函数表,然后根据函数表进行分派。
Listing 3: student.h
struct Student; typedef void (*Student_print_name)(struct Student *); typedef void (*Student_set_name)(struct Student *, const char *); typedef void (*Student_print_score)(struct Student *s); typedef void (*Student_set_score)(struct Student *s, int score); typedef int (*Student_get_score)(struct Student *s); typedef int (*Student_compare_score)(struct Student *lhs, struct Student *rhs); typedef void (*Student_destroy)(struct Student *s); struct StudentInterface { Student_print_name print_name; Student_set_name set_name; Student_print_score print_score; Student_set_score set_score; Student_get_score get_score; Student_compare_score compare_score; Student_destroy destroy; }; typedef void (*CheatStudent_cheat)(struct CheatStudent *cs); struct CheatStudentInterface { struct StudentInterface base; CheatStudent_cheat cheat; }; void Student_class_init(); struct Student* Student_create(); struct CheatStudent; void CheatStudent_class_init(); struct CheatStudent* CheatStudent_create(); #define AS_INTERFACE(INTERFACE, POINTER_TO_INSTANCE) ((INTERFACE *) *((void**)POINTER_TO_INSTANCE)) #define CREATE_DERIVED_INSTANCE(BASE_TYPE, CREATOR) (BASE_TYPE) (CREATOR())
Listing 4: student.c
#include <stdio.h> #include <stdlib.h> #include "student.h" struct Object { void* interface; }; struct Student { struct Object object; char name[16]; int score; }; struct StudentInterface _student_interface; void _student_print_name(struct Student *s) { printf("%s\n", s->name); } void _student_set_name(struct Student *s, const char *name) { strncpy(s->name, name, 16); } void _student_print_score(struct Student *s) { printf("%d\n", s->score); } void _student_set_score(struct Student *s, int score) { s->score = score; } int _student_get_score(struct Student *s) { return s->score; } int _student_compare_score(struct Student *lhs, struct Student *rhs) { int a = lhs->score; int b = rhs->score; if (a < b) { return -1; } else if (a == b) { return 0; } else { return 1; } } void _student_destroy(struct Student *student) { free(student); } struct Student* Student_create() { struct Student *s = (struct Student*) malloc(sizeof(struct Student)); if (s == NULL) { return NULL; } s->object.interface = &_student_interface; return s; } void Student_class_init() { _student_interface.print_name = _student_print_name; _student_interface.print_score = _student_print_score; _student_interface.set_name = _student_set_name; _student_interface.set_score = _student_set_score; _student_interface.get_score = _student_get_score; _student_interface.compare_score = _student_compare_score; _student_interface.destroy = _student_destroy; } struct CheatStudent { struct Student base; int cheated; }; void _cheat_student_cheat(struct CheatStudent* cs) { AS_INTERFACE(struct StudentInterface, cs)->set_score(cs, 100); cs->cheated = 1; } struct CheatStudentInterface _cheatstudent_interface; struct CheatStudent* CheatStudent_create() { struct CheatStudent *s = (struct CheatStudent*) malloc(sizeof(struct CheatStudent)); if (s == NULL) { return NULL; } s->base.object.interface = &_cheatstudent_interface; s->cheated = 0; return s; } void _cheatstudent_print_score(struct CheatStudent *s) { int score = AS_INTERFACE(struct StudentInterface, &(s->base))->get_score(&(s->base)); printf("%d%s\n", score, s->cheated ? "(cheat)" : ""); } void CheatStudent_class_init() { memcpy(&_cheatstudent_interface.base, _student_interface, sizeof(_student_interface)); _cheatstudent_interface.base.print_score = _cheatstudent_print_score; _cheatstudent_interface.cheat = _cheat_student_cheat; }
Listing 5: main.c
#include <stdio.h> #include <stdlib.h> #include "student.h" int main() { Student_class_init(); struct Student *student = Student_create(); AS_INTERFACE(struct StudentInterface, student)->set_name(student, "Alice"); AS_INTERFACE(struct StudentInterface, student)->print_name(student); AS_INTERFACE(struct StudentInterface, student)->set_score(student, 90); AS_INTERFACE(struct StudentInterface, student)->print_score(student); AS_INTERFACE(struct StudentInterface, student)->destroy(student); CheatStudent_class_init(); struct CheatStudent *cheat_student = CREATE_DERIVED_INSTANCE(struct Student*, CheatStudent_create); AS_INTERFACE(struct StudentInterface, cheat_student)->set_name(cheat_student, "Bob"); AS_INTERFACE(struct StudentInterface, cheat_student)->print_name(cheat_student); AS_INTERFACE(struct StudentInterface, cheat_student)->set_score(cheat_student, 90); AS_INTERFACE(struct StudentInterface, cheat_student)->print_score(cheat_student); AS_INTERFACE(struct CheatStudentInterface, cheat_student)->cheat(cheat_student); AS_INTERFACE(struct StudentInterface, cheat_student)->print_score(cheat_student); AS_INTERFACE(struct StudentInterface, cheat_student)->destroy(cheat_student); return 0; }
Listing 6: 输出
Alice 90 Bob 90 100(cheat)
这篇关于使用C语言实现面向对象编程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-10Kafka GUI 客户端推荐Kafka-King v0.32-icode9专业技术文章分享
- 2024-11-10ES GUI 客户端推荐ES-King v0.13-icode9专业技术文章分享
- 2024-11-09uniapp中的父类组件怎么获取子类组件的内容信息?-icode9专业技术文章分享
- 2024-11-09超大数据量从云端迁移到本地的可行性方案是什么?-icode9专业技术文章分享
- 2024-11-09想在web项目直接预览word excel pdf ppt 等文件有哪些方法?-icode9专业技术文章分享
- 2024-11-09uniapp 在 开发 app 中,怎么在 video 暂停时获取到当前暂停帧的图片-icode9专业技术文章分享
- 2024-11-08阿里云ECS项目实战:新手入门教程
- 2024-11-08阿里云ECS资料入门教程
- 2024-11-08Nacos配置中心资料详解:新手入门教程
- 2024-11-08405 Not Allowed错误是什么,怎么排查解决-icode9专业技术文章分享