C语言面向对象


什么是面向对象(OOP)

对象 实体 -> 一类事物的个体
类型 类别
特征:
抽象:对事物进行抽象,属性(一类事物的共同特征)、行为(一类事物共同的行为)。
封装:将抽象出来的属性和行为组合起来,封装成为一个类(结构体)
继承:基类(父类) -> 派生类,子类继承自父类,子类就有了父类的所有属性和行为
多态:在父子对象之间,可以发生多态行为。当有父类指针指向子类对象时,调用父类的行为,会调用子类的行为,从而对同一消息做出不同反应。

封装函数

用结构体+pthis指针封装函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
typedef struct Girl
{
int age;
char name[20];
}Girl;
Girl* createGirl();
Girl* createGirlWithArgs(int age, const char name[20]);
void girl_destroy(Girl* pthis);
void girl_print(Girl* pthis);

int main()
{
Girl* emptyGirl = createGirl();
Girl* yue = createGirlWithArgs(25, "yue");

return 0;
}

Girl* createGirl()
{
Girl* girl = malloc(sizeof(Girl));
if(!Girl)
{
return NULL;
}
return girl;
}
Girl* createGirlWithArgs(int age, const char name[20])
{
Girl* girl = malloc(sizeof(Girl));
if(!Girl)
{
return NULL;
}
girl->age = age;
strcpy(girl->name, name);
return girl;
}
void girl_destroy(Girl* pthis)
{
free(pthis);
}
void girl_print(Girl* pthis)
{
printf("%d %s\n", pthis->age, pthis-name);
}

封装成员函数

利用函数指针封装行为函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//提前声明可以调用
typedef struct Girl Girl;
typedef struct Girl
{
//属性
int age;
char name[20];
//行为
void (*destroy)(Girl* pthis);
void (*print)(Girl* pthis);
}Girl;
//同时要在创建时进行初始化
Girl* createGirl()
{
Girl* girl = malloc(sizeof(Girl));
if(!Girl)
{
return NULL;
}
girl->print = girl_print;
girl->destroy = girl_destroy;
return girl;
}

int main()
{
Girl* yue = createGirlWithArgs(25, "yue");
girl_printf(yue);
yue->print(yue);
return 0;
}

隐藏pthis指针

用全局变量pthis代替传参,加上带参宏,可实现隐藏传参

多线程可能不安全,感觉不如显式传参

1
2
3
4
5
6
7
8
Girl* pthis = NULL;
#define G_Cast(object) (pthis = (object))
int main()
{
Girl* yue = createGirlWithArgs(25, "yue");
G_Cast(yue)->print();
return 0;
}

隐藏属性

通过函数来访问成员变量

1
2
3
4
5
6
7
8
9
int girl_getage()
{
return pthis->age;
}
void girl_setage(int age)
{
pthis->age = age;
}
// ...

通过在定义一个结构体来封装变量

1
2
3
4
struct inner{
int age;
char name[20];
};

封装多文件

通过不同的文件封装不同的类
将inner定义在c文件中,可避免h文件被使用时,调用inner接口访问内部变量

继承实现

其实就是结构体内包含结构体。
两种方式访问,可以直接调用(a.b->c),要对基类比较了解,也可以通过调用基类函数调用(B(a)->c或C(B(a)))。

多态实现

当父类指针指向子类对象时,他会去调用子类函数,而不是父类函数
实现方法虚函数表:
1.定义一个虚函数表结构体
2.将需要的函数填入结构体
3.在初始化对象时,填入指定的函数到虚函数表中
4.通过父类函数传入子类对象指针时,通过虚函数表调用函数,而虚函数表指向的是子类的函数

注意虚函数表必须初始化之后才能调用,不然访问野指针了。
父类自身的函数和虚函数表的函数不一致,同样是虚函数表指向实现函数
子类在被继承,通过子类调用孙类还要再实现一遍虚函数表,而通过父类可以
意义在于可以统一接口

多态多文件

一种类一个文件