1 简介
1.1 什么是 OOC?
OOC(Object Oriented C) 是一个高效、轻量的面向对象的 ANSI-C 扩展,用于支持ANSI-C面向对象的软件开发。它支持
- 封装:隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读取和修改的访问级别。
- 单继承,多接口继:根据现有类定义新类和行为的能力。
- 多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。。
1.2 为何要用 OOC?
“Program to interfaces, not to implementations” 已经成为面向对象编程的共识,它可以有效的减小系统之间的依赖,提高软件的开发质量。然而目前依然有很多嵌入式的平台缺乏面向对象语言的工具链,ANSI-C 依然是很对嵌入式平台的主流开发语言,因此,如果能在c语言中找到一种模拟继承和多态性的方法,就可在这种非面向对象的语言中实现 OO 的设计模式,提升软件的开发质量。
2 OOC 如何实现?
简而言之,OOC使用宏来描述和使用类。下面深入讲解 OOC 的具体实现。
2.1 封装
在本节中,我将解释OOC框架如何实现封装。我将介绍一个简单类的创建过程,它的数据隐藏是通过命名约定执行的。
假设我们想要写一个程序来打印某个公司员工的信息,因此我们定义一个类来收集员工的所有信息。
1
2
3
4
5
6
7
8
9
10
11
12
// employee.h
#ifndef __EMPLOYEE_H__
#define __EMPLOYEE_H__
struct Employee
{
const char *szName;
float salary;
};
void PrintEmployee(struct Employee *pEm);
#endif
它的简单实现如下:
1
2
3
4
5
6
7
8
9
// employee.c
#include <stdio.h>
#include "employee.h"
void PrintEmployee(struct Employee *pEm)
{
printf("Name: %s, Salary: %f", pEm->szName, pEm->salary);
}
很显然,上面的实现由几个缺点:
- 缺乏构造和析构函数,需要使用者自己初始化和清理结构体里的成员,从而导致代码中可能会有多处构造和析构的代码,增加维护的难度。
- 一个好的C开发人员会添加函数来初始化结构体并在结束的时候清理它,但这仍然不能保证用户一定会调用它。
因此,封装是我们所需要的。通过使用统一的编码规则和命名约定,我们可以再在C 中模拟类的实现。
- 类属性都是结构中的成员。
- 类方法 是 C 函数, 它第一个参数是指向该属性结构的指针,称它为self。
- 为了进一步加强属性、类方法和类之间的关系,我们使用统一的命名规则。类方法是类名与操作名称的连接,用下划线分隔,例如Employee_Print。这个简单的命名约定还可以防止不同类方法之间的命名冲突。
- 访问控制是使用命名约定处理的另一方面。大多数OO语言提供了以下级别的保护:
- private: 只能从类中访问,变量名以两个下划线”__“开始。
- protected: 由类及其后代访问,变量名以单个下划线”_“开始。
- public: 均可访问
采用上述规则,再应用到我们的例子中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// employee.h
#ifndef __EMPLOYEE_H__
#define __EMPLOYEE_H__
typedef struct Employee
{
const char *__szName;
float __salary;
} Employee;
typedef Employee* PEmployee;
EM_Stat Employee_Con(PEmployee self, const char *szName, float salary);
void Employee_Print(PEmployee self);
EM_Stat Employee_Des(PEmployee self);
#define OOC_Employee_Print(self) Employee_Print(self)
#define OOC_Employee_GetSalary(self) (((PEmployee)(self))->__salary)
#define OOC_Employee_GetName(self) (((PEmployee)(self))->__szName)
#define OOC_Employee_Des(self) Employee_Des(self)
#endif
上述类的定义中,使用Employee_Con来构造 Employee 对象,使用Employee_Des来析构它。每个类必须至少有一个构造函数。析构函数是可选的,但即使没有声明析构函数,也必须使用OOC_ClassName_Des函数来析构类的实例。这个类的简单实现如下:
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
// employee.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
#include "employee.h"
EM_Stat Employee_Con(PEmployee self, const char *szName, float salary)
{
self->__szName = malloc(strlen(szName)+1);
if(self->__szName == NULL)
return EM_ERR;
strcpy((char *)self->__szName, szName);
self->__salary = salary;
return EM_OK;
}
void Employee_Print(PEmployee self)
{
printf("Name: %s, Salary: %f", self->__szName, self->__salary);
}
EM_Stat Employee_Des(PEmployee self)
{
free((void *)self->__szName);
return EM_OK;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// main.c
#include <stdio.h>
#include "util.h"
#include "employee.h"
int main()
{
Employee em;
if(Employee_Con(&em, "Bob", 10000.0))
{
float salary;
salary = OOC_Employee_GetSalary(&em);
OOC_Employee_Print(&em);
}
OOC_Employee_Des(&em);
return 0;
}
编译运行,输出如下:
1
Name: Bob, Salary: 10000.000000
2.2 单继承
类的封装是以一些编程规则和命名约定来实现的,那么类的继承该如何实现呢?在 OOC 框架中,多重继承无法实现,但是我们可以实现单继承和多接口继承,本节我们将详细介绍单继承的实现。
回到刚才的例子,我们实现了一个可以打印员工信息的类,经理作为一个特殊的员工,他具有和普通员工相同的属性,但他有一个额外的属性:级别(level),如果要打印经理的信息,很显然,我们需要创建一个从 Employee 继承的类,因此借鉴 C++中虚函数的实现,并且重新定义 Print 函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// manager.h
#ifndef __MANAGER_H__
#define __MANAGER_H__
typedef struct Manager
{
Employee super;
int __level;
} Manager;
typedef Manager* PManager;
EM_Stat Manager_Con(PManager self, const char *szName, float salary, int level);
void Manager_Print(PManager self);
#define OOC_Manager_Print(self) Manager_Print(self)
#define OOC_Manager_GetSalary(self) (((PManager)(self))->super.__salary)
#define OOC_Manager_GetName(self) (((PManager)(self))->super.__szName)
#define OOC_Manager_Des(self) Employee_Des(self)
#endif
类的定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// manager.c
#include <stdio.h>
#include "util.h"
#include "employee.h"
#include "manager.h"
EM_Stat Manager_Con(PManager self, const char *szName, float salary, int level)
{
if(EM_ERR == Employee_Con(&self->super, szName, salary))
{
return EM_ERR;
}
self->__level = level;
return EM_OK;
}
void Manager_Print(PManager self)
{
Employee_Print(&self->super);
printf(", Level: %d", self->__level);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// main.c
#include <stdio.h>
#include "util.h"
#include "employee.h"
#include "manager.h"
int main()
{
Manager ma;
if(Manager_Con(&ma, "Bob", 10000.0, 7))
{
float salary;
salary = OOC_Employee_GetSalary(&ma);
printf("Inheritance test: Salary--%f\n", salary);
OOC_Manager_Print(&ma);
}
OOC_Manager_Des(&ma);
return 0;
}
编译运行,输出为
1
2
Inheritance test: Salary--10000.000000
Name: Bob, Salary: 10000.000000, Level: 7
上述例子中,可以看出,Manager 这个类它继承了 Employee 的所有属性和方法,并且重新定义了 Print 函数,增加了打印 level 的功能。
OOC 中继承实现的关键:父类的结构体定义必须是子类结构体的第一个成员,这样父类的结构体和子类结构体就有相同的首地址,从而保证将子类指针赋给父类指针时对成员访问的正确性。
2.3 多态
上个例子的实现中,虽然实现单继承,但是依然存在一个严重的问题:将子类的指针赋给父类指针的调用有问题,例如:将 Manager 的指针赋给 Employee 时,如果调用 Print ,调用的是父类的方法,而非子类的方法。
1
OOC_Employee_Print(&ma);
输出为
1
Name: Bob, Salary: 10000.000000
那么多态该如何实现呢?C++的多态是通过虚表来实现的,OOC也是通过类似的方法类实现多态。
首先,我们定义一个全局的父类,即所有的类都必须继承于它,它只包含一个成员:虚表。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// ooc.h
#ifndef __OOC_H__
#define __OOC_H__
typedef struct Object * PObject;
typedef struct ObjectClass *PObjectClass;
struct Object
{
PObjectClass __vptr;
};
struct ObjectClass
{
EM_Stat (*Des)(PObject);
};
EM_Stat Object_Con(PObject self);
EM_Stat Object_Des(PObject self);
#define OOC_VCALL(OX, CX, MX)\
(*((P##CX##Class)(((PObject)(OX))->__vptr))->MX)(/*(P##CX)*/(OX))
#endif
上述代码中,Object 是全局的父类,而 ObjectClass是 Object 类中虚表的具体定义,它其实就是由一堆函数指针构成。而 Employee 继承于 Object,因此也就继承了 Object 的虚表,它的定义如下:
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
// employee.h
#ifndef __EMPLOYEE_H__
#define __EMPLOYEE_H__
typedef struct Employee* PEmployee;
typedef struct EmployeeClass * PEmployeeClass;
struct Employee
{
struct Object super;
const char *__szName;
float __salary;
};
struct EmployeeClass
{
struct ObjectClass super;
void (*Print)(PEmployee );
float (*GetSalary)(PEmployee );
};
EM_Stat Employee_Con(PEmployee self, const char *szName, float salary);
void Employee_Print(PEmployee self);
float Employee_GetSalary(PEmployee self);
EM_Stat Employee_Des(PEmployee self);
#define OOC_Employee_Print(self) OOC_VCALL(self, Employee, Print)
#define OOC_Employee_GetSalary(self) OOC_VCALL(self, Employee, GetSalary)
#define OOC_Employee_Des(self) OOC_VCALL(&(self)->super, Object, Des)
#endif
EmployeeClass继承于ObjectClass,并扩展了一些自己的方法。类在实例化时,构造函数应该初始化它的虚表。
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
48
// employee.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
#include "ooc.h"
#include "employee.h"
const struct EmployeeClass __employee={
{
Employee_Des
},
Employee_Print,
Employee_GetSalary
};
EM_Stat Employee_Con(PEmployee self, const char *szName, float salary)
{
EM_Stat err = EM_OK;
err = Object_Con(&self->super);
if(EM_ERR == err)
return err;
((PObject)self)->__vptr = (PObjectClass)&__employee;
self->__szName = malloc(strlen(szName)+1);
if(self->__szName == NULL)
return EM_ERR;
strcpy((char *)self->__szName, szName);
self->__salary = salary;
return EM_OK;
}
void Employee_Print(PEmployee self)
{
printf("Name: %s\n\tSalary: %f\n", self->__szName, self->__salary);
}
float Employee_GetSalary(PEmployee self)
{
return self->__salary;
}
EM_Stat Employee_Des(PEmployee self)
{
free((void *)self->__szName);
return EM_OK;
}
类的实现中,我们首先应该实现该类的虚表__employee,并在构造函数中初始化。那么在调用 OOC_CLASS_METHOD 时,通过函数指针,就可以调用虚表中指定的函数。
下面给出 Manager 的定义和实现以及相应的测试代码:
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
// manager.h
#ifndef __MANAGER_H__
#define __MANAGER_h__
typedef struct Manager* PManager;
typedef struct ManagerClass* PManagerClass;
struct Manager
{
struct Employee super;
int __level;
};
struct ManagerClass
{
struct EmployeeClass super;
int (*GetLevel)(PManager );
};
EM_Stat Manager_Con(PManager self, const char *szName, float salary, int level);
void Manager_Print(PManager self);
int Manager_GetLevel(PManager self);
EM_Stat Manager_Des(PManager self);
#define OOC_Manager_Print(self) OOC_VCALL(&(self)->super, Employee, Print)
#define OOC_Manager_GetLevel(self) OOC_VCALL(self, Manager, GetLevel)
#define OOC_Manager_GetSalary(self) OOC_VCALL(&(self)->super, Employee, GetSalary)
#define OOC_Manager_Des(self) OOC_VCALL(&(self)->super.super, Object, Des)
#endif
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
// manager.c
#include <stdio.h>
#include "util.h"
#include "ooc.h"
#include "employee.h"
#include "manager.h"
const struct ManagerClass __manager={
{
{
Employee_Des
},
Manager_Print,
Employee_GetSalary
},
Manager_GetLevel
};
EM_Stat Manager_Con(PManager self, const char *szName, float salary, int level)
{
if(EM_ERR == Employee_Con(&self->super, szName, salary))
{
return EM_ERR;
}
((PObject)self)->__vptr = (PObjectClass)&__manager;
self->__level = level;
return EM_OK;
}
void Manager_Print(PManager self)
{
Employee_Print(&self->super);
printf("\tLevel: %d\n", self->__level);
}
int Manager_GetLevel(PManager self)
{
return self->__level;
}
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
// main.c
#include <stdio.h>
#include "util.h"
#include "ooc.h"
#include "employee.h"
#include "manager.h"
int main()
{
EM_Stat err;
struct Employee em;
struct Manager ma;
PEmployee pEm[2];
err = Employee_Con(&em, "Jim", 5000.0);
err = Manager_Con(&ma, "Bob", 10000.0, 7);
pEm[0] = &em;
pEm[1] = &ma;
{
int i;
for(i = 0; i < 2; i++)
OOC_Employee_Print(pEm[i]);
}
OOC_Manager_Des(&ma);
OOC_Employee_Des(&em);
return 0;
}
编译运行,得到如下结果:
1
2
3
4
5
Name: Jim
Salary: 5000.000000
Name: Bob
Salary: 10000.000000
Level: 7
可以看到,我们使用父类的指针,实现了子类方法的调用。
2.4 多接口继承
从2.2小节我们知道,类的继承中,父类的结构体必须是子类结构体的第一个成员,这也就决定了 OOC 中类只能单继承。
但如果一个类只定义了抽象方法(纯虚函数),没有定义任何属性,那么这样的类可以只用它的虚表来表示,从这个类继承也就只需要维护它的虚表,当然,还需要实现所有的抽象方法。而一个对象可以很容易地维护许多这样的指针,因此实现这种特定类(接口)的多继承是很容易实现的,无非就是多维护几个虚表。
回到我们的例子,假设我们的打印员工信息的程序非常成功,我们被要求把打印函数做成通用的接口,从而供其他程序使用,并且需要提供一个比较员工薪资的接口,作为一个优秀的程序员,我们希望写一个通用的比较和打印函数,它们可以处理不同类型的类。
所以我们需要重写 Employee 类,它需要继承于 Object 类,同时继承于 IPrint 和 IComparable这两个接口类,如下:
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
// employee.h
#ifndef __EMPLOYEE_H__
#define __EMPLOYEE_H__
typedef struct Employee* PEmployee;
typedef struct EmployeeClass * PEmployeeClass;
struct Employee
{
struct Object super;
PIComparable IComparable;
PIPrint IPrint;
const char *__szName;
float __salary;
};
struct EmployeeClass
{
struct ObjectClass super;
struct IComparable IComparable;
struct IPrint IPrint;
float (*GetSalary)(PEmployee );
const char *(*GetName)(PEmployee );
};
EM_Stat Employee_Con(PEmployee self, const char *szName, float salary);
int Employee_Compare(PEmployee self, PEmployee other);
void Employee_Print(PEmployee self);
float Employee_GetSalary(PEmployee self);
const char * Employee_GetName(PEmployee self);
EM_Stat Employee_Des(PEmployee self);
#define OOC_Employee_Print(self) OOC_ICALL(&(self)->IPrint, Print))
#define OOC_Employee_Compare(self, other) OOC_ICALL(&(self)->IComparable, Compare), (other))
#define OOC_Employee_GetSalary(self) OOC_VCALL(self, Employee, GetSalary)
#define OOC_Employee_GetName(self) OOC_VCALL(self, Employee, GetName)
#define OOC_Employee_Des(self) OOC_VCALL(&(self)->super, Object, Des)
#endif
同时,我们给出 IPrint 和 IComparable以及相关的宏定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
// iprint.h
#ifndef __IPRINT_h__
#define __IPRINT_h__
typedef struct IPrint * PIPrint;
struct IPrint
{
void (*Print)(PObject);
int __offset;
};
#define OOC_IPrint_Print(self) OOC_ICALL((self), Print) OOC_END_ICALL
#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// icomparable.h
#ifndef __ICOMPARABLE_H__
#define __ICOMPARABLE_H__
typedef struct IComparable * PIComparable;
struct IComparable
{
int (*Compare)(PObject self, PObject other);
int __offset;
};
#define OOC_IComparable_Compare(self, other) OOC_ICALL((self), Compare), (other) OOC_END_ICALL
#endif
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
// ooc.h
#ifndef __OOC_H__
#define __OOC_H__
typedef struct Object * PObject;
typedef struct ObjectClass *PObjectClass;
struct Object
{
PObjectClass __vptr;
};
struct ObjectClass
{
EM_Stat (*Des)(PObject);
};
EM_Stat Object_Con(PObject self);
EM_Stat Object_Des(PObject self);
#define OOC_VCALL(OX, CX, MX)\
(*((P##CX##Class)(((PObject)(OX))->__vptr))->MX)(/*(P##CX)*/(OX))
#define OOC_ICALL(IPX, MX) (*(*(IPX))->MX)(OOC_I_TO_OBJ(IPX)
#define OCC_END_ICALL )
#define OOC_I_TO_OBJ(IPX) ((PObject)(void*)((char*)(IPX)-(*(IPX))->__offset))
#endif
此时,假如我们调用OOC_Employee_Compare,传入 Compare 函数的是 IComparable 类型的指针,那么该如何正确的访问到 Employee 的类成员呢?很简单,通过 IComparable 里的__offset 成员,它记录了 Employee 类中,IComparable 成员首地址与 Employee 首地址的偏差,因此将 IComparable 的指针地址减去__offset 就可以得到 Employee 的首地址,下面这条宏定义就是用于实现此功能:
1
#define OOC_I_TO_OBJ(IPX) ((PObject)(void*)((char*)(IPX)-(*(IPX))->__offset))
理解多接口继承的原理后,我们给出对应的接口实现和测试代码:
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// employee.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include "util.h"
#include "ooc.h"
#include "icomparable.h"
#include "iprint.h"
#include "employee.h"
const struct EmployeeClass __employee={
{
Employee_Des
},
{
Employee_Compare,
offsetof(struct Employee, IComparable)
},
{
Employee_Print,
offsetof(struct Employee, IPrint)
},
Employee_GetSalary,
Employee_GetName
};
EM_Stat Employee_Con(PEmployee self, const char *szName, float salary)
{
EM_Stat err = EM_OK;
err = Object_Con(&self->super);
if(EM_ERR == err)
return err;
((PObject)self)->__vptr = (PObjectClass)&__employee;
self->IComparable = (struct IComparable *) &__employee.IComparable;
self->IPrint = (struct IPrint *) &__employee.IPrint;
self->__szName = malloc(strlen(szName)+1);
if(self->__szName == NULL)
return EM_ERR;
strcpy((char *)self->__szName, szName);
self->__salary = salary;
return EM_OK;
}
int Employee_Compare(PEmployee self, PEmployee other)
{
int res = 0;
if(self->__salary > other->__salary)
res = 1;
else if(self->__salary < other->__salary)
res = -1;
return res;
}
void Employee_Print(PEmployee self)
{
printf("Name: %s\n\tSalary: %f\n", self->__szName, self->__salary);
}
float Employee_GetSalary(PEmployee self)
{
return self->__salary;
}
const char * Employee_GetName(PEmployee self)
{
return self->__szName;
}
EM_Stat Employee_Des(PEmployee self)
{
free((void *)self->__szName);
return EM_OK;
}
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
48
49
50
51
52
53
54
55
// manager.c
#include <stdio.h>
#include <stddef.h>
#include "util.h"
#include "ooc.h"
#include "icomparable.h"
#include "iprint.h"
#include "employee.h"
#include "manager.h"
const struct ManagerClass __manager={
{
{
Employee_Des
},
{
Employee_Compare,
offsetof(struct Manager, super.IComparable)
},
{
Manager_Print,
offsetof(struct Manager, super.IPrint)
},
Employee_GetSalary,
Employee_GetName
},
Manager_GetLevel
};
EM_Stat Manager_Con(PManager self, const char *szName, float salary, int level)
{
if(EM_ERR == Employee_Con(&self->super, szName, salary))
{
return EM_ERR;
}
((PObject)self)->__vptr = (PObjectClass)&__manager;
self->super.IComparable = (PIComparable) &__manager.super.IComparable;
self->super.IPrint = (PIPrint) &__manager.super.IPrint;
self->__level = level;
return EM_OK;
}
void Manager_Print(PManager self)
{
Employee_Print(&self->super);
printf("\tLevel: %d\n", self->__level);
}
int Manager_GetLevel(PManager self)
{
return self->__level;
}
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
48
49
50
51
52
53
// main.c
#include <stdio.h>
#include "util.h"
#include "ooc.h"
#include "icomparable.h"
#include "iprint.h"
#include "employee.h"
#include "manager.h"
int main()
{
EM_Stat err;
struct Employee em;
struct Manager ma;
PEmployee pEm[2];
err = Employee_Con(&em, "Jim", 5000.0);
err = Manager_Con(&ma, "Bob", 10000.0, 7);
pEm[0] = &em;
pEm[1] = &ma;
{
int i;
for(i = 0; i < 2; i++)
{
OOC_Employee_Print(pEm[i]);
}
}
int res = OOC_Employee_Compare(pEm[0], pEm[1]);
printf("%s's salary(%f) is",
OOC_Employee_GetName(pEm[0]),
OOC_Employee_GetSalary(pEm[0]));
if(res > 0)
{
printf(" higher than ");
}
else if(res < 0)
{
printf(" lower than ");
}
else
{
printf(" equal to ");
}
printf("%s(%f)\n",
OOC_Employee_GetName(pEm[1]),
OOC_Employee_GetSalary(pEm[1]));
OOC_Manager_Des(&ma);
OOC_Employee_Des(&em);
return 0;
}
编译运行,输出为:
1
2
3
4
5
6
Name: Jim
Salary: 5000.000000
Name: Bob
Salary: 10000.000000
Level: 7
Jim's salary(5000.000000) is lower than Bob(10000.000000)
上述的多接口继承还存在一个问题:假设类 C 继承了两个接口 IA 和 IB,但是 IA 和 IB 都有一个相同名称的函数 Foo,此时 类 C 在接口实现的时候就会有命名冲突, 出现两个 C_Foo 函数,这该如何解决呢?很简单,我们可以在类 C的实现中重命名 FOO 函数,将从 IA 继承的 Foo 接口重命名为 C_FooFromA,从 IB 继承的 Foo 接口重命名为 C_FooFromB,在对应接口虚表初始化的时候,初始化为对应的函数即可。
3 总结
OOC 的本质就是 C 语言里的强制类型转换和指针的巧妙运用,特别是函数指针,它可以说是 OOC 实现的核心所在,理解了函数指针,OOC 也就很容易理解了。
第一篇博文,文章某些地方描述可能不够清晰、准确,有不当之处,欢迎指正。
Reference
- Booch: Object-Oriented Analysis and Design With Applications, Second Edition. Addison-Wesley, 1994
- Gamma, R. Helm, R. Johnson and J. Vlissides: Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995
- Meyer: Object-Oriented Software Construction. Second Edition, Prentice Hall, 1997
- Buschmann, R. Meunier, H. Rohnert, P. Sommerlad, M. Stal : Pattern–Oriented Software Architecture, a System of Patterns. Wiley 1996
- Portable Inheritance and Polymorphism in C : The original document from Miro Samek that appeared on the Embedded.com website in February 1997(详细描述了 OOC 宏的实现)