面向对象的思想
c语言没有在语言层面支持面向对象的特性,但是可以通过结构体和函数模拟面向对象的思想。
在Python中,可以通过class
关键字实现面向对象的写法
class User:
""" 定义类 """
def __init__(
self,
username: str,
password: str
):
""" 初始化类 """
self.username = username
self.password = password
def user_print(self):
""" 定义类方法 """
return f'User(username={self.username}, password={self.password})'
if __name__ == '__main__':
user = User('admin', '123456')
print(user.user_print()) # User(username=admin, password=123456)
上面的self
其实是一个指向当前对象的指针,在Python中,编译器会自动将user
传入user_print
函数中,所以我们可以直接调用user_print
函数。
在C语言中,我们需要手动传入user
指针,所以我们需要将user_print
函数的第一个参数改为struct User*
类型。
// 定义类
struct User {
char* username;
char* password;
};
// 初始化类
struct User*
user_init(char* username, char* password)
{
struct User* user = malloc(sizeof(struct User));
user->username = strdup(username);
user->password = strdup(password);
return user;
}
// 定义类方法
void
user_print(struct User* user)
{
printf("User(username=%s, password=%s)\n", user->username, user->password);
}
int
main()
{
struct User* user = user_init("admin", "123456");
user_print(user);
free(user->username);
free(user->password);
free(user);
return 0;
}
通过对比可以发现,Python中的类和C语言中的结构体+函数的写法非常相似,只是Python中的类和类方法的定义更加简洁。
如何实现继承
在Python中,我们可以通过class
关键字的()
来实现继承,通过指定父类的类名来实现继承。
class VIPUser(User):
""" 定义类 """
def __init__(
self,
username: str,
password: str,
level: int
):
""" 初始化类 """
super().__init__(username, password)
self.level = level
由于在C语言中,并没有类的概念,优雅的实现继承非常困难。下面介绍几种实现继承的方法。
通过结构体嵌套实现继承
// 定义父类
struct User {
char* username;
char* password;
};
// 定义子类
struct VIPUser {
struct User user;
int level;
};
// 初始化子类
struct VIPUser*
vip_user_init(char* username, char* password, int level)
{
struct VIPUser* vip_user = malloc(sizeof(struct VIPUser));
vip_user->user.username = strdup(username);
vip_user->user.password = strdup(password);
vip_user->level = level;
return vip_user;
}
通过结构体嵌套的方式,我们可以实现继承,但是这种方式有一个缺点,在访问父类的属性时,需要加上user
前缀,比较麻烦。
通过宏实现继承
将子类与父类的属性合并到一起,通过宏定义放在结构体的最前面,这样就可以直接访问父类的属性。cpython的PyObject
和lua
语言中GCObject
结构体,都是通过宏定义实现继承的。
// 定义父类
#define UserHeader \
char* username; \
char* password
// 定义父类
struct User {
UserHeader;
};
// 定义子类
struct VIPUser {
UserHeader;
int level;
};
// 初始化子类
struct VIPUser*
vip_user_init(char* username, char* password, int level)
{
struct VIPUser* vip_user = malloc(sizeof(struct VIPUser));
vip_user->username = strdup(username);
vip_user->password = strdup(password);
vip_user->level = level;
return vip_user;
}
这种实现方法相比于第一种,可以直接访问共同的(父类)属性,而不需要加上user
前缀。通过在父类中添加类型标注信息,可以实现多态,即处理不同的对象时,调用相同的方法,但是会根据对象的类型,调用不同的方法。
typedef enum {
USER_TYPE,
VIP_USER_TYPE,
} UserType;
// 定义父类
#define UserHeader \
char* username; \
char* password; \
UserType type;
添加type
成员,用来标识对象的类型,然后在初始化对象时,将type
成员设置为对应的类型。
// 定义父类
typedef struct {
UserHeader;
} User;
// 定义子类
typedef struct {
UserHeader;
int level;
} VIPUser;
// 当是普通用户时,调用这个函数
void
default_print_user(User* user)
{
printf("User(username=%s, password=%s)\n", user->username, user->password);
}
// 当是 VIP 用户时,调用这个函数
void
vip_print_user(VIPUser* user)
{
printf("VIPUser(username=%s, password=%s, level=%d)\n", user->username,
user->password, user->level);
}
// User的打印函数
// 可以同时打印普通用户和 VIP 用户
void
print_user(User* user)
{
switch (user->type) {
case USER_TYPE:
default_print_user(user);
break;
case VIP_USER_TYPE:
vip_print_user((VIPUser*)user);
break;
}
}