结构体替代类
使用结构体来封装变量和函数,即可实现类似对象的功能。其中,结构体包含变量和函数指针,变量用于存储成员变量的值,函数指针用于实现成员函数的功能。而每个对象的变量是独立的,因此可以使用这种方法实现类似对象的功能。
下面是一个例子,以封装一个“人”的结构体为例:
typedef struct { char *name; int age; void (*speak)(const char *); } Person; void speak_func(const char *str) { printf("%s\n", str); } Person *new_person(char *name, int age) { Person *p = malloc(sizeof(Person)); p->name = name; p->age = age; p->speak = speak_func; return p; } void destroy_person(Person *p) { free(p); } void main() { Person *p = new_person("Tom", 18); p->speak("Hello world!"); //输出:Hello world! destroy_person(p); }
上述例子中,使用结构体Person封装了“人”的信息,其中包含两个成员变量和一个成员函数指针。函数指针speak指向函数speak_func,用于输出“人”的话语。通过new_person()函数创建一个新的“人”对象,人对象的变量是独立存储的。最后通过destroy_person()函数销毁“人”对象。
继承和多态
继承是面向对象的一个重要特性,允许子类继承父类的成员变量和成员函数,并可以自行定义新的成员变量和成员函数,因此可以实现代码的重用。
多态是面向对象中的另一个重要特性,指父类指针可以指向子类对象,从而调用子类重写的成员函数,实现代码的灵活性。
在C语言中,可以通过定义不同的结构体实现类的继承关系,并使用函数指针来实现多态。
下面是一个例子,以封装一个“动物”的父类和两个子类“狗”和“猫”为例:
typedef struct _Animal { const char *name; void (*speak)(struct _Animal *); } Animal; void speak(Animal *animal) { printf("%s speaks\n", animal->name); } typedef struct { Animal base; } Dog; void dog_speak(Dog *dog) { printf("%s barks\n", dog->base.name); } void init_dog(Dog *dog, const char *name) { dog->base.name = name; dog->base.speak = (void (*)(Animal *)) dog_speak; } typedef struct { Animal base; } Cat; void cat_speak(Cat *cat) { printf("%s meows\n", cat->base.name); } void init_cat(Cat *cat, const char *name) { cat->base.name = name; cat->base.speak = (void (*)(Animal *)) cat_speak; } void main() { Dog d; Cat c; init_dog(&d, "Dog"); init_cat(&c, "Cat"); Animal *pDog = (Animal*)(&d); Animal *pCat = (Animal*)(&c); pDog->speak(pDog); //输出:Dog barks pCat->speak(pCat); //输出:Cat meows }
上述例子中,定义了一个“动物”的父类和两个子类“狗”和“猫”。通过定义不同的结构体实现类的继承关系。其中,“动物”的父类包含成员变量name和成员函数指针speak,成员函数speak使用结构体指针作为参数,指向不同的子类实现。
通过init_dog()和init_cat()函数分别初始化“狗”和“猫”对象,其中“狗”对象继承了“动物”的父类,并自定义了成员函数speak()的实现,输出“狗”叫声;“猫”对象同理。
最后通过定义Animal类型的指针指向Dog和Cat实例实现多态。pDog和pCat指向不同的子类实例,调用speak函数时分别输出“狗”声和“猫”声。