在C语言中模拟OOP构造Animal实例:能否省略手动赋值construct函数指针的步骤?
在C中模拟OOP:隐藏构造函数指针赋值,实现单语句实例构造
你遇到的问题很典型——在C里模拟类的时候,不想让用户手动去设置构造函数的函数指针,希望像高级语言那样用一行代码完成实例化和初始化。我们可以通过工厂函数或者封装初始化逻辑来解决这个问题,下面给你两种可行的方案:
方案1:使用工厂函数(动态分配实例)
这种方式最接近高级语言里的new关键字,直接返回一个已经完全初始化好的Animal实例,用户不需要手动处理任何函数指针赋值。
修改后的animal.h
#ifndef ANIMAL_H #define ANIMAL_H #include <stdio.h> #include <string.h> typedef struct Animal Animal; // 工厂函数:创建并初始化Animal实例 Animal* Animal_create(char *name, unsigned int weight); // 可选:添加销毁函数,释放动态分配的内存 void Animal_destroy(Animal *self); struct Animal { // 属性 char name[50]; unsigned int weight; // 方法 void (*move)(Animal *, char*); unsigned int (*get_name_length)(Animal *); }; #endif
修改后的animal.c
#include "animal.h" #include <stdlib.h> static void Animal_move(Animal *self, char *moveType) { printf("%s is moving (%s)...\n", self->name, moveType); } static unsigned int Animal_get_name_length(Animal *self) { printf("Getting %s name length...\n", self->name); return strlen(self->name); } // 内部初始化逻辑,封装所有函数指针赋值 static void Animal_init(Animal *self, char *name, unsigned int weight) { strcpy(self->name, name); self->weight = weight; self->move = Animal_move; self->get_name_length = Animal_get_name_length; } Animal* Animal_create(char *name, unsigned int weight) { // 动态分配内存 Animal *new_animal = malloc(sizeof(Animal)); if (new_animal == NULL) { perror("Failed to allocate memory for Animal"); return NULL; } // 调用内部初始化函数 Animal_init(new_animal, name, weight); return new_animal; } void Animal_destroy(Animal *self) { if (self != NULL) { free(self); } }
修改后的main.c
#include "animal.h" int main(void) { // 单语句完成实例创建+初始化 Animal *dog = Animal_create("Doge", 8); if (dog == NULL) { return 1; } // 调用方法 dog->move(dog, "walking"); printf("%s weight property is %u kilograms.\n", dog->name, dog->weight); printf("%s name has %u characters.\n", dog->name, dog->get_name_length(dog)); // 记得释放内存 Animal_destroy(dog); return 0; }
方案2:封装初始化函数(栈上分配实例)
如果你不想用动态内存分配,希望在栈上创建实例,可以把初始化逻辑封装成一个函数,用户只需要声明实例,然后调用这个初始化函数即可,同样不需要手动设置函数指针。
修改后的animal.h
#ifndef ANIMAL_H #define ANIMAL_H #include <stdio.h> #include <string.h> typedef struct Animal Animal; // 初始化函数:初始化已声明的Animal实例 void Animal_init(Animal *self, char *name, unsigned int weight); struct Animal { // 属性 char name[50]; unsigned int weight; // 方法 void (*move)(Animal *, char*); unsigned int (*get_name_length)(Animal *); }; #endif
修改后的animal.c
#include "animal.h" static void Animal_move(Animal *self, char *moveType) { printf("%s is moving (%s)...\n", self->name, moveType); } static unsigned int Animal_get_name_length(Animal *self) { printf("Getting %s name length...\n", self->name); return strlen(self->name); } void Animal_init(Animal *self, char *name, unsigned int weight) { strcpy(self->name, name); self->weight = weight; self->move = Animal_move; self->get_name_length = Animal_get_name_length; }
修改后的main.c
#include "animal.h" int main(void) { // 声明实例 + 单语句初始化 Animal dog; Animal_init(&dog, "Doge", 8); // 调用方法 dog.move(&dog, "walking"); printf("%s weight property is %u kilograms.\n", dog.name, dog.weight); printf("%s name has %u characters.\n", dog.name, dog.get_name_length(&dog)); return 0; }
为什么这两种方案能解决你的问题?
- 我们把函数指针赋值和属性初始化的逻辑都封装到了
Animal_create或Animal_init函数里,用户完全不需要接触到内部的函数指针设置,就像使用高级语言的构造函数一样。 - 方案1的工厂函数还帮你处理了内存分配,避免了手动管理内存的麻烦(记得用完调用
Animal_destroy释放);方案2则保留了栈分配的轻量性,适合不需要长期存活的实例。
另外,注意你原来的代码里有个拼写错误:weigth应该是weight,我在修改后的代码里已经修正了这个问题。
内容的提问来源于stack exchange,提问作者Citra Dewi
相关产品推荐
相关产品推荐

