c 如何实现继承c语言

99ANYc3cd6
预计阅读时长 24 分钟
位置: 首页 C语言 正文

下面我将分步解释如何实现,从最基础的继承到更高级的多态。

c 如何实现继承c语言
(图片来源网络,侵删)

核心思想

C语言实现继承的核心思想是 “组合”“函数指针”

  1. 组合:我们用一个“子结构体”来包含一个“父结构体”作为它的第一个成员,这模拟了“is-a”(是一个)的关系,一个 Dog 是一个 Animal
  2. 函数指针:在结构体中,我们使用函数指针来表示方法,子结构体可以“重写”父结构体中的方法指针,从而实现多态。

第1步:实现基础的继承

假设我们有一个基类 Animal 和一个派生类 Dog

定义“基类” - Animal

在C中,我们用 struct 来模拟类,它包含数据成员和函数指针成员。

// animal.h
#ifndef ANIMAL_H
#define ANIMAL_H
// 前向声明,因为Dog结构体中会用到Animal
typedef struct Animal Animal;
typedef struct Dog Dog;
// Animal的虚函数表
// 这是一个关键结构,它存储了所有Animal的方法(函数指针)
typedef struct {
    void (*say)(Animal*); // say方法的函数指针
} AnimalVTable;
// Animal结构体本身
struct Animal {
    const AnimalVTable* vtable; // 指向虚函数表的指针
};
// Animal的构造函数
void Animal_ctor(Animal* this);
#endif // ANIMAL_H

定义“派生类” - Dog

Dog 结构体的第一个成员必须是 Animal,这确保了在内存布局上,Dog 对象可以被视为 Animal 对象(通过类型转换)。

c 如何实现继承c语言
(图片来源网络,侵删)
// dog.h
#ifndef DOG_H
#define DOG_H
#include "animal.h"
// Dog的虚函数表
// 它可以继承并扩展Animal的虚函数表
typedef struct {
    void (*say)(Animal*); // 重写了Animal的say方法
} DogVTable;
// Dog结构体
struct Dog {
    Animal base; // 第一个成员必须是基类,实现"is-a"关系
    // Dog特有的数据成员
    char breed[50];
};
// Dog的构造函数
void Dog_ctor(Dog* this, const char* breed);
#endif // DOG_H

实现方法

我们来实现这些方法,注意,Dogsay 方法会接收一个 Animal* 指针,但我们会把它转换成 Dog* 来访问 Dog 特有的数据。

// animal.c
#include "animal.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Animal的say方法实现
static void Animal_say(Animal* this) {
    printf("Animal makes a sound.\n");
}
// Animal的虚函数表实例
static const AnimalVTable animal_vtable = {
    .say = Animal_say
};
// Animal的构造函数
void Animal_ctor(Animal* this) {
    this->vtable = &animal_vtable;
}
// dog.c
#include "dog.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Dog的say方法实现
static void Dog_say(Animal* this) {
    // 将Animal指针转换回Dog指针
    Dog* dog = (Dog*)this;
    printf("Dog of breed %s says: Woof!\n", dog->breed);
}
// Dog的虚函数表实例
// 它重写了Animal的say方法
static const DogVTable dog_vtable = {
    .say = Dog_say
};
// Dog的构造函数
void Dog_ctor(Dog* this, const char* breed) {
    // 1. 首先调用基类的构造函数
    Animal_ctor(&this->base);
    // 2. 设置自己的虚函数表(这里重写了say方法)
    // 注意:在更复杂的场景下,我们可能会先复制父类的vtable,然后修改特定方法
    // 为了简化,我们直接创建一个新的vtable
    this->base.vtable = (const AnimalVTable*)&dog_vtable;
    // 3. 初始化自己的特有数据
    strncpy(this->breed, breed, sizeof(this->breed) - 1);
    this->breed[sizeof(this->breed) - 1] = '\0';
}

使用

我们可以在 main 函数中使用它们了。

// main.c
#include "animal.h"
#include "dog.h"
#include <stdio.h>
void make_sound(Animal* a) {
    a->vtable->say(a); // 通过虚函数表调用正确的方法
}
int main() {
    // 创建一个Dog实例
    Dog my_dog;
    Dog_ctor(&my_dog, "Golden Retriever");
    // 创建一个Animal实例
    Animal my_animal;
    Animal_ctor(&my_animal);
    printf("--- Calling make_sound function ---\n");
    make_sound(&my_animal); // 传入Animal指针,调用Animal::say
    make_sound((Animal*)&my_dog); // 传入Dog指针(向上转型),调用Dog::say
    printf("\n--- Direct access ---\n");
    my_animal.vtable->say(&my_animal);
    my_dog.base.vtable->say((Animal*)&my_dog); // 也可以直接通过base访问
    return 0;
}

编译和运行:

gcc main.c animal.c dog.c -o inheritance_example
./inheritance_example

输出:

c 如何实现继承c语言
(图片来源网络,侵删)
--- Calling make_sound function ---
Animal makes a sound.
Dog of breed Golden Retriever says: Woof!
--- Direct access ---
Animal makes a sound.
Dog of breed Golden Retriever says: Woof!

从输出可以看到,make_sound 函数接收一个 Animal*,但根据传入对象的真实类型(Animal 还是 Dog),它调用了正确的 say 方法,这就是多态


第2步:实现多重继承

多重继承在C中要复杂得多,因为需要处理内存布局问题,特别是当多个父类有相同偏移的成员时,核心思想是在子结构体中包含多个父结构体

假设 Dog 还想继承 Pet 特性。

// pet.h
#ifndef PET_H
#define PET_H
#include <stdio.h>
typedef struct Pet Pet;
typedef struct PetVTable {
    void (*play)(Pet*);
} PetVTable;
struct Pet {
    const PetVTable* vtable;
};
void Pet_ctor(Pet* this);
void Pet_play(Pet* this);
#endif
// pet.c
#include "pet.h"
static void Pet_play(Pet* this) {
    printf("Pet is playing.\n");
}
static const PetVTable pet_vtable = {
    .play = Pet_play
};
void Pet_ctor(Pet* this) {
    this->vtable = &pet_vtable;
}

现在修改 Dogdog.h

// dog.h (修改后)
#ifndef DOG_H
#define DOG_H
#include "animal.h"
#include "pet.h"
// Dog的虚函数表,需要同时包含Animal和Pet的方法
typedef struct {
    void (*say)(Animal*);
    void (*play)(Pet*); // 添加了Pet的方法
} DogVTable;
// Dog结构体,包含两个基类
struct Dog {
    Animal base;       // 第一个基类
    Pet pet_base;      // 第二个基类
    char breed[50];
};
void Dog_ctor(Dog* this, const char* breed);
#endif
// dog.c (修改后)
#include "dog.h"
#include <stdio.h>
#include <string.h>
// Dog的say方法
static void Dog_say(Animal* this) {
    Dog* dog = (Dog*)this;
    printf("Dog of breed %s says: Woof!\n", dog->breed);
}
// Dog的play方法
static void Dog_play(Pet* this) {
    Dog* dog = (Dog*)this;
    printf("Dog of breed %s is playing fetch!\n", dog->breed);
}
// Dog的虚函数表
static const DogVTable dog_vtable = {
    .say = Dog_say,
    .play = Dog_play
};
void Dog_ctor(Dog* this, const char* breed) {
    // 1. 初始化所有基类
    Animal_ctor(&this->base);
    Pet_ctor(&this->pet_base);
    // 2. 设置vtable
    // 注意:这里简化了,在真实编译器中,vtable会更复杂,需要处理多个基类的偏移。
    this->base.vtable = (const AnimalVTable*)&dog_vtable;
    this->pet_base.vtable = (const PetVTable*)&dog_vtable;
    // 3. 初始化特有数据
    strncpy(this->breed, breed, sizeof(this->breed) - 1);
    this->breed[sizeof(this->breed) - 1] = '\0';
}

使用多重继承:

int main() {
    Dog my_dog;
    Dog_ctor(&my_dog, "Labrador");
    printf("--- Calling methods through vtables ---\n");
    my_dog.base.vtable->say((Animal*)&my_dog);
    my_dog.pet_base.vtable->play((Pet*)&my_dog);
    return 0;
}

总结与优缺点

优点

  1. 灵活性:可以在纯C环境中实现面向对象的设计模式,如继承和多态。
  2. 性能:这种实现方式通常非常高效,因为它只是结构体和函数指针的调用,没有C++中虚函数表查找的额外开销(现代C++的虚函数调用也很快)。
  3. 可移植性:不依赖任何特定的C++编译器,在任何标准C环境下都可以工作。

缺点

  1. 复杂性:代码变得非常繁琐和冗长,你需要手动维护虚函数表、构造函数链等。
  2. 类型安全:C的类型系统无法提供C++那样的保护,你必须手动进行类型转换((Dog*)this),容易出错。
  3. 可读性差:对于不熟悉这种模式的开发者来说,代码难以理解。
  4. 缺乏语言支持:没有 public, private, protected 访问控制,也没有构造函数/析构函数的自动调用。

虽然C语言没有内置的继承机制,但通过 结构体组合函数指针(虚函数表),我们可以手动模拟出继承和多态的行为,这种方法在C语言的大型项目(如Linux内核、GTK+ GUI库、游戏引擎等)中被广泛使用,以构建模块化、可扩展的代码。

对于新的项目,如果条件允许,直接使用C++会是更简单、更安全、更易维护的选择,但如果你必须使用C,那么理解并掌握这种手动实现继承的技巧是一项非常有价值的技能。

-- 展开阅读全文 --
头像
万网空间如何安装dedecms?
« 上一篇 04-09
dede图集注释如何调用?
下一篇 » 04-09

相关文章

取消
微信二维码
支付宝二维码

目录[+]