面向对象的基本特性:封装,继承,多态
1.0 面向过程概念
当我们在编写程序时,通常采用以下步骤:
1.
将问题的解法分解成若干步骤
2.
使用函数分别实现这些步骤
3.
依次调用这些函数
这种编程风格的被称作
面向过程
。除了
面向过程
之外,还有一种被称作
面向对象
的编程风格被广泛使
用。
面向对象
采用基于对象的概念建立模型,对现实世界进行模拟,从而完成对问题的解决。
C
语言的语法并不直接支持面向对象风格的编程。但是,我们可以通过额外的代码,让
C
语言实现一些面向对象特性。
2.0 程序案例
#define _CRT_SECURE_NO_WARNINGS
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
struct student
{
int id;
char name[20];
int gender;
int mark;
};
int MakeStudentId(int year, int classNum, int serialNum)
{
// 创建一个char类型的数组
char buffer[20];
// 将三个变量转换为指定格式的字符串,存储在buffer数组中
sprintf(buffer, "%d%d%d", year, classNum, serialNum);
// 将字符串转换为整数
int id = atoi(buffer);
// 返回项目的id值
return id;
}
const char* NumGenderToStrGender(int numGender)
{
if (numGender == 0)
{
return "女";
}
else if (numGender == 1)
{
return "男";
}
return "NULL";
}
int StrGenderToNumGender(const char* strGender)
{
int numGender;
if (strcmp("男", strGender) == 0)
{
numGender = 1;
}
else if (strcmp("女", strGender) == 0)
{
numGender = 0;
}
else
{
numGender = -1;
}
return numGender;
}
int main()
{
// 创建结构体变量
struct student stu;
stu.id = MakeStudentId(2024, 123, 26);
strcpy(stu.name, "小明");
stu.gender = StrGenderToNumGender("男");
stu.mark = 98;
printf("学号:%d\n", stu.id);
printf("姓名: %s\n", stu.name);
const char* gender = NumGenderToStrGender(stu.gender);
printf("性别:%s\n", gender);
printf("分数:%s\n", stu.mark);
return 0;
}
现在,我们使用
面向过程
风格写了
3
个函数和一个结构体,并且调用了这些函数,将函数返回的结果赋值给了结构体。接下来,让我们以面向对象风格来重新审视这段代码。
3.0 面向对象
现在,我们使用
面向过程
风格写了
3
个函数和一个结构体,并且调用了这些函数,将函数返回的结果赋值给了结构体。接下来,让我们以面向对象风格来重新审视这段代码。
在面向对象风格中,结构体被看做
数据(
data
)
,而操作数据的函数称作
方法(
method
)
。目前函数和数据是分离的,函数并不直接操作数据,我们需要拿到函数返回的结果,再将其赋值给数据。
面向对
象风格编程的第一大特性
---
封装
,它希望
方法直接操作数据
,并且将数据和方法
结合
在一起,它们构成
一个整体,
而这个整体被称作
对象
。
此外,还有一个方法命名上的规则。一般来说,
获取数据的方法会被命名为 getXXX ,设置数据的方法 会被命名为 setXXX 。
成员id的表示方式:
1. 将函数的第一个参数设置为 struct student * ,让函数直接操作 student 结构体。
2.
修改函数名,获取数据的方法命名为
getXXX
,设置数据的方法命名为
setXXX
。
4.0 封装特性
我们来看看学校里面最重要的主体是什么?是学生,学生肯定拥有很多属性,比如学生的学号、姓名、性别、考试分数等等。自然地,我们会声明一个结构体用于表示学生。
typedef struct
{
int id;
char name[20];
int gender;
int mark;
}StudentInfo_t;
注:将需要的信息封装为一个结构体内部包含学生的姓名,学号,性别,分数。
通过函数设置学生的id编号,函数可以通过结构体指针,直接操作结构体中的数据
void SetStudentId(StudentInfo_t* s, int year, int classNum, int serialNum)
{
char buffer[20];
sprintf(buffer, "%d%d%d", year, classNum, serialNum);
int id = atoi(buffer);
s->id = id;
}
获取学生的性别函数,在面向对象的编程方法中获取数据的函数被我们设置为GetXXX,设置数据的函数被我们设置为SetXXX。
const char* GetGender(StudentInfo_t* s)
{
if (s->gender == 0)
{
return "女";
}
else if (s->gender == 1)
{
return "男";
}
return "未知";
}
设置数据的方法
void SetGender(StudentInfo_t* s, const char* strGender)
{
int numGender;
if (strcmp("男", strGender) == 0)
{
numGender = 1;
}
else if (strcmp("女", strGender) == 0)
{
numGender = 0;
}
else
{
numGender = -1;
}
s->gender = numGender;
}
通过主函数进行调用
int main()
{
StudentInfo_t stu;
SetStudentId(&stu, 2022, 123, 26);
strcpy(stu.name, "小明");
SetGender(&stu, "男");
stu.mark = 98;
// 打印这些数值
printf("学号:%d\n", stu.id);
printf("姓名:%s\n", stu.name);
const char* gender = GetGender(&stu);
printf("性别:%s\n", gender);
printf("分数:%d\n", stu.mark);
}
完整函数代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
int id;
char name[20];
int gender;
int mark;
}StudentInfo_t;
void SetStudentId(StudentInfo_t* s, int year, int classNum, int serialNum)
{
char buffer[20];
sprintf(buffer, "%d%d%d", year, classNum, serialNum);
int id = atoi(buffer);
s->id = id;
}
const char* GetGender(StudentInfo_t* s)
{
if (s->gender == 0)
{
return "女";
}
else if (s->gender == 1)
{
return "男";
}
return "未知";
}
void SetGender(StudentInfo_t* s, const char* strGender)
{
int numGender;
if (strcmp("男", strGender) == 0)
{
numGender = 1;
}
else if (strcmp("女", strGender) == 0)
{
numGender = 0;
}
else
{
numGender = -1;
}
s->gender = numGender;
}
int main()
{
StudentInfo_t stu;
SetStudentId(&stu, 2022, 123, 26);
strcpy(stu.name, "小明");
SetGender(&stu, "男");
stu.mark = 98;
// 打印这些数值
printf("学号:%d\n", stu.id);
printf("姓名:%s\n", stu.name);
const char* gender = GetGender(&stu);
printf("性别:%s\n", gender);
printf("分数:%d\n", stu.mark);
}
目前,函数可以直接操作数据了。但是,函数和数据依然是两个独立的部分。我们要将
函数和数据结合
到一起,这样,这个整体就能被称作
对象
,函数可以称作属于这个对象的
方法
。
当前我们可以吧结构体理解为我们的数据,函数可以理解为我们的方法,数据和方法结合在一起可以称之为对象
对象.方法(对象指针,参数1,参数2, 参数3...)
接下来,我们举几个这种格式的例子:
stu.setGender(&stu, "男");
以上代码中,对象为
stu
,方法为
setGender
。通过
对象
+
点
+
方法
的形式,可以调用属于对
象
stu
的
setGender
方法。在方法的参数中传入性别
男
。这样,方法会把性别
男
转换为整形,并设置到对象
stu
的数据当中。
const char* gender = stu.getGender(&stu);
以上代码中,
对象为 stu ,方法为 getGender 。
通过对象 + 点 + 方法的形式,可以调用属于对
象 stu 的 getGender 方法。
getGender
方法从对象数据中获取整形表示的性别,并返回性别对应的字符
你好编程
串。
在
C
语言中,
若要实现对象 + 点 + 方法的形式,我们可以借助于函数指针。
在结构中,声明这3个函数的函数指针。
5.0 面向对象
在结构体中声明函数指针
struct student {
void (*setStudentId)(struct student* s, int year, int classNum, int serialNum);
const char* (*getGender)(struct student* s);
void (*setGender)(struct student* s, const char* strGender);
int id; // 学号
char name[20]; // 姓名
int gender; // 性别
int mark; // 分数
};
这个时候可以将结构体作为一个对象看待,使用对象(结构体变量). 方法(函数)的方式进行参数的赋值和调用。
生成学生的id
void setStudentId(struct student* s, int year, int classNum, int serialNum)
{
char buffer[20];
sprintf(buffer, "%d%d%d", year, classNum, serialNum);
int id = atoi(buffer);
s->id = id;
}
获取学生的性别
const char* getGender(struct student* s)
{
if (s->gender == 0)
{
return "女";
}
else if (s->gender == 1)
{
return "男";
}
return "未知";
}
设置学生的性别
void setGender(struct student* s, const char* strGender)
{
int numGender;
if (strcmp("男", strGender) == 0)
{
numGender = 1;
}
else if (strcmp("女", strGender) == 0)
{
numGender = 0;
}
else
{
numGender = -1;
}
s->gender = numGender;
}
初始化函数指针【对象初始化之后才能被调用】
void initStudent(struct student* s)
{
s->setStudentId = setStudentId;
s->getGender = getGender;
s->setGender = setGender;
}
主函数相关代码
int main()
{
struct student stu;
// 初始化student
initStudent(&stu);
stu.setStudentId(&stu, 2022, 123, 26);
strcpy(stu.name, "小明");
stu.setGender(&stu, "男");
stu.mark = 98;
// 打印这些数值
printf("学号:%d\n", stu.id);
printf("姓名:%s\n", stu.name);
const char* gender = stu.getGender(&stu);
printf("性别:%s\n", gender);
printf("分数:%d\n", stu.mark);
}
完整代码
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct student {
void (*setStudentId)(struct student* s, int year, int classNum, int serialNum);
const char* (*getGender)(struct student* s);
void (*setGender)(struct student* s, const char* strGender);
int id; // 学号
char name[20]; // 姓名
int gender; // 性别
int mark; // 分数
};
void setStudentId(struct student* s, int year, int classNum, int serialNum)
{
char buffer[20];
sprintf(buffer, "%d%d%d", year, classNum, serialNum);
int id = atoi(buffer);
s->id = id;
}
const char* getGender(struct student* s)
{
if (s->gender == 0)
{
return "女";
}
else if (s->gender == 1)
{
return "男";
}
return "未知";
}
void setGender(struct student* s, const char* strGender)
{
int numGender;
if (strcmp("男", strGender) == 0)
{
numGender = 1;
}
else if (strcmp("女", strGender) == 0)
{
numGender = 0;
}
else
{
numGender = -1;
}
s->gender = numGender;
}
void initStudent(struct student* s)
{
s->setStudentId = setStudentId;
s->getGender = getGender;
s->setGender = setGender;
}
int main()
{
struct student stu;
// 初始化student
initStudent(&stu);
stu.setStudentId(&stu, 2022, 123, 26);
strcpy(stu.name, "小明");
stu.setGender(&stu, "男");
stu.mark = 98;
// 打印这些数值
printf("学号:%d\n", stu.id);
printf("姓名:%s\n", stu.name);
const char* gender = stu.getGender(&stu);
printf("性别:%s\n", gender);
printf("分数:%d\n", stu.mark);
}
6.0 继承基本概念
除了学生之外,学校里面还需要有老师,老师也具有很多属性。例如:
- 工号
- 姓名
- 性别
- 任课科目
声明一个结构体用于表示老师。
struct teacher
{
int id; // 工号
char name[20]; // 姓名
int gender; // 性别
char subject[20]; // 任课科目
};
比较一下学生和老师的结构体,看看它们之间有什么共同之处与不同之处。
struct teacher {
int id; // 工号
char name[20]; // 姓名
int gender; // 性别
char subject[20]; // 任课科目
};
struct student {
int id; // 学号
char name[20]; // 姓名
int gender; // 性别
int mark; // 分数
};
共同之处如下:
- 编号
- 姓名
- 性别
不同之处:
- 学生有考试分数
- 老师有任课科目
我们可以把两个结构体中的共同之处
抽象
出来,让它共同之处成为一个新的结构。这个结构体具有老师
和学生的共性,而老师与学生它们都是人,可以把这个结构体命名为
person
。
struct person{
int id; // 编号
char name[20]; // 姓名
int gender; // 性别
};
接下来,我们可以让老师和学生结构包含这个
person
对象。
struct teacher {
struct person super;
char subject[20]; // 任课科目
};
struct student {
struct person super;
int mark; // 分数
};
让我们比较一下原有代码与现有代码
// 原有代码
struct teacher {
int id; // 工号
char name[20]; // 姓名
int gender; // 性别
char subject[20]; // 任课科目
};
struct student {
int id; // 学号
char name[20]; // 姓名
int gender; // 性别
int mark; // 分数
};
// 现有代码
struct person{
int id; // 编号
char name[20]; // 姓名
int gender; // 性别
};
struct teacher {
struct person super;
char subject[20]; // 任课科目
};
struct student {
struct person super;
int mark; // 分数
};
原有代码中,老师和学生结构体中,均有
id
、
name
、
gender
三个变量。现有代码中,将这
3
个变量抽象成结构体
person
。这样一来,有两个好处:
- 1. 减少重复代码
- 2. 代码层次更清晰
由于
student
和
teacher
拥有
person
的一切,因此,我们可以说,
student
与
teacher
均
继承
于
person
。
person 是 student 与 teacher 的父对象。 student 与 teacher 是 person 的子对象。
刚刚我们只讨论了数据,现在我们结合上方法一起讨论
struct person{
int id; // 编号
char name[20]; // 姓名
int gender; // 性别
};
struct teacher {
struct person super;
char subject[20]; // 任课科目
};
struct student {
struct person super;
int mark; // 分数
void (*setStudentId)(struct student* s, int year, int classNum, int serialNum);
const char* (*getGender)(struct student* s);
void (*setGender)(struct student* s, const char* strGender);
};
之前我们为
student
写了
3
个方法
- 设置性别
- 获取性别
- 设置学号
其中,性别相关的方法也属于共性的方法。可以把这两个函数指针移动到
person
对象里面去,注意,要把方法的第一个参数
struct student *
修改为
struct person *
。移动后,子对
象
student
与
teacher
均可以使用这一对性别相关的方法。而设置学号的方法,为
student
独有的方
法,因此保持不变,依然将其放置在
student
对象内。
创建一个Person方法,内部包含
struct person
{
int id;
char name[20];
int gender;
const char* (*getGender)(struct student* s);
void (*setGender)(struct student* s, const char* strGender);
};
struct teacher
{
// 创建结构体成员变量
struct person super;
char subject[20];
};
struct student {
void (*setStudentId)(struct student* s, int year, int classNum, int serialNum);
struct person super;
int mark; // 分数
};
对应上面的更改,函数
getGender
与
setGender
的第一个参数也要由
struct student *
修改
为
struct person *
。
const char* getGender(struct person* p)
{
if (p->gender == 0)
{
return "女";
}
else if (p->gender == 1)
{
return "男";
}
return "未知";
}
void setGender(struct person* p, const char* strGender)
{
int numGender;
if (strcmp("男", strGender) == 0)
{
numGender = 1;
}
else if (strcmp("女", strGender) == 0)
{
numGender = 0;
}
else
{
numGender = -1;
}
p->gender = numGender;
}
此外,
setStudentId
函数中,
id
成员,不在
student
中,而是在
student
中的
person
中。这里也要对应的修改一下。
void setStudentId(struct student* s, int year, int classNum, int serialNum)
{
char buffer[20];
sprintf(buffer, "%d%d%d", year, classNum, serialNum);
int id = atoi(buffer);
s->super.id = id;
}
还有,别忘了给结构初始化函数指针。
void initPerson(struct person* p)
{
p->getGender = getGender;
p->setGender = setGender;
}
void initStudent(struct student* s)
{
initPerson(&(s->super));
s->setStudentId = setStudentId;
}
void initTeacher(struct teacher* t)
{
initPerson(&(t->super));
}
main函数调用
int main()
{
struct student stu;
// 初始化student
initStudent(&stu);
stu.setStudentId(&stu, 2022, 123, 26);
strcpy(stu.super.name, "小明");
stu.super.setGender(&stu.super, "男");
stu.mark = 98;
// 打印这些数值
printf("学号:%d\n", stu.super.id);
printf("姓名:%s\n", stu.super.name);
const char* gender = stu.super.getGender(&stu.super);
printf("性别:%s\n", gender);
printf("分数:%d\n", stu.mark);
putchar('\n');
struct teacher t;
// 初始化teacher
initTeacher(&t);
t.super.id = 12345;
strcpy(t.super.name, "林老师");
t.super.setGender(&t.super, "男");
strcpy(t.subject, "C语言");
// 打印这些数值
printf("学号:%d\n", t.super.id);
printf("姓名:%s\n", t.super.name);
gender = t.super.getGender(&t.super);
printf("性别:%s\n", gender);
printf("科目:%s\n", t.subject);
}
完整代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct person
{
int id;
char name[20];
int gender;
const char* (*getGender)(struct student* s);
void (*setGender)(struct student* s, const char* strGender);
};
struct teacher
{
// 创建结构体成员变量
struct person super;
char subject[20];
};
struct student {
void (*setStudentId)(struct student* s, int year, int classNum, int serialNum);
struct person super;
int mark; // 分数
};
void setStudentId(struct student* s, int year, int classNum, int serialNum)
{
char buffer[20];
sprintf(buffer, "%d%d%d", year, classNum, serialNum);
int id = atoi(buffer);
s->super.id = id;
}
const char* getGender(struct person* p)
{
if (p->gender == 0)
{
return "女";
}
else if (p->gender == 1)
{
return "男";
}
return "未知";
}
void setGender(struct person* p, const char* strGender)
{
int numGender;
if (strcmp("男", strGender) == 0)
{
numGender = 1;
}
else if (strcmp("女", strGender) == 0)
{
numGender = 0;
}
else
{
numGender = -1;
}
p->gender = numGender;
}
void initPerson(struct person* p)
{
p->getGender = getGender;
p->setGender = setGender;
}
void initStudent(struct student* s)
{
initPerson(&(s->super));
s->setStudentId = setStudentId;
}
void initTeacher(struct teacher* t)
{
initPerson(&(t->super));
}
int main()
{
struct student stu;
// 初始化student
initStudent(&stu);
stu.setStudentId(&stu, 2022, 123, 26);
strcpy(stu.super.name, "小明");
stu.super.setGender(&stu.super, "男");
stu.mark = 98;
// 打印这些数值
printf("学号:%d\n", stu.super.id);
printf("姓名:%s\n", stu.super.name);
const char* gender = stu.super.getGender(&stu.super);
printf("性别:%s\n", gender);
printf("分数:%d\n", stu.mark);
putchar('\n');
struct teacher t;
// 初始化teacher
initTeacher(&t);
t.super.id = 12345;
strcpy(t.super.name, "林老师");
t.super.setGender(&t.super, "男");
strcpy(t.subject, "C语言");
// 打印这些数值
printf("学号:%d\n", t.super.id);
printf("姓名:%s\n", t.super.name);
gender = t.super.getGender(&t.super);
printf("性别:%s\n", gender);
printf("科目:%s\n", t.subject);
}
程序运行结果
7.0 多态
struct Rect {
void (*draw)(struct Rect *);
int left;
int top;
int right;
int bottom;
};
struct Circle {
void (*draw)(struct Circle *);
int x;
int y;
int r;
};
struct Triangle {
void (*draw)(struct Triangle *);
POINT p1;
POINT p2;
POINT p3;
};
我们仔细观察这
3
个对象,看看它们分别有什么共性?可以发现,这
3
个对象,它们都有一个
draw
方法。那么,我们可以将
draw
这个方法抽象出来,单独放置到一个对象当中。由于这三个对象都是形
状。我们可以把单独抽象出来的对象,命名为
shape
。
shape
对象中的
draw
方法,应当是一个共性的方
法,所以,它的参数应当设置为
struct Shape *
。
struct Shape {
void (*draw)(struct Shape *);
};
这是共性的结构体,可以称之为结构体对象
接下来,让
Rect
、
Circle
、
Triangle
三个对象分别都包含
Shape
对象。这样,它们就都能使
用
draw
这个方法了。
struct Rect {
struct Shape super;
int left;
int top;
int right;
int bottom;
};
struct Circle {
struct Shape super;
int x;
int y;
int r;
};
struct Triangle {
struct Shape super;
POINT p1;
POINT p2;
POINT p3;
};
这里有一个需要注意的地方,
父对象与子对象的内存排布必须重合。
例如:下图中,上面的两个对象内存排布可以重合。而下面的两个对象的内存排布无法重合。
如果父对象和子对象的内存排布不重合会出现错误
像下面一样的声明
Rect
是正确的。
// 正确
struct Rect {
struct Shape super;
int left;
int top;
int right;
int bottom;
};
而下面一样的声明
Rect
是错误的。
// 错误
struct Rect {
int left;
int top;
int right;
int bottom;
struct Shape super;
};
接着,我们需要修改各对象的初始化函数。将原有的
r->draw
改为
r->super.draw
。
void initRect(struct Rect* r)
{
r->super.draw = drawRect;
}
void initCircle(struct Circle* c)
{
c->super.draw = drawCircle;
}
void initTriangle(struct Triangle* t)
{
t->super.draw = drawTriangle;
}
注意,这里还有一个问题,函数内赋值运算符左边的函数指针
r->super.draw
的类型
为
void (*)(struct Shape*)
,参数为
struct Shape *
。而赋值运算符右边的函数指针类型分别为:
- void (*)(struct Rect*)
- void (*)(struct Circle*)
- void (*)(struct Triangle*)
函数指针参数类型不一致,无法进行赋值。我们可以把右边的函数指针强制类型转换为
void (*)(struct Shape*)
。
void initRect(struct Rect* r)
{
r->super.draw = (void (*)(struct Shape*))drawRect;
}
void initCircle(struct Circle* c)
{
c->super.draw = (void (*)(struct Shape*))drawCircle;
}
void initTriangle(struct Triangle* t)
{
t->super.draw = (void (*)(struct Shape*))drawTriangle;
}
我们考虑一下怎样来使用这些对象。
struct Rect r = { {}, - 200, 200, 200, 0 };
struct Circle c = { {},0, 0, 100 };
struct Triangle t = { {}, {0, 200}, {-200, 0}, {200, 0} };
首先,声明
Rect
、
Circle
、
Triangle
这
3
个对象,并使用初始化列表将其初始化。注意,由于它们的第一个成员为
super
,所以,这里使用空列表
{}
,将
super
成员初始化为零。
initRect(&r);
initCircle(&c);
initTriangle(&t);
让三个对象分别调用各自的初始化函数,给各自对象 super 成员中的 draw 设置为各自对应的绘图函数。
r.super.draw 设置为 drawRect
c.super.draw 设置为 drawCircle
t.super.draw 设置为 drawRTriangle
struct Shape *arrShape[3] = {
(struct Shape *)&r, (struct Shape*)&c, (struct Shape*)&t};
声明一个元素类型为
struct Shape *
的数组,元素个数为
3
。分别用
r
的指针,
c
的指针,
t
的指针初始化。注意,这里也需要进行强制类型转换,否则初始化列表里面的指针类型和数组元素的指针类型不
一致。
for (int i = 0; i < 3; i++)
{
arrShape[i]->draw(arrShape[i]);
}
到了关键的一步,使用循环,依次调用 draw 函数。由于3次循环中的 draw 函数分别为各个图形各自的 绘图函数。所以,虽然统一调用的是 draw ,但是,却可以执行它们各自的绘图函数。至此,不同实现 的方法,在此得到统一。
完整代码实现
#define _CRT_SECURE_NO_WARNINGS
#include <stdint.h>
#include <stdio.h>
#include <easyx.h>
#include <stdlib.h>
struct Shape
{
void (*draw)(struct Shape*);
};
struct Rect
{
struct Shape super;
int left;
int top;
int right;
int bottom;
};
struct Circle
{
struct Shape super;
int x;
int y;
int r;
};
struct Triangle
{
struct Shape super;
POINT p1;
POINT p2;
POINT p3;
};
void drawRect(struct Rect* r)
{
rectangle(r->left, r->top, r->right, r->bottom);
}
void drawCircle(struct Circle* c)
{
circle(c->x, c->y, c->r);
}
void drawTriangle(struct Triangle* t)
{
line(t->p1.x, t->p1.y, t->p2.x, t->p2.y);
line(t->p2.x, t->p2.y, t->p3.x, t->p3.y);
line(t->p3.x, t->p3.y, t->p1.x, t->p1.y);
}
void InitRect(struct Rect* r)
{
r->super.draw = (void(*)(struct Shape*)) drawRect;
}
void InitCircle(struct Circle* c)
{
c->super.draw = (void(*)(struct Shape*))drawCircle;
}
void InitTriangle(struct Triangle* t)
{
t->super.draw = (void(*)(struct Shape*))drawTriangle;
}
int main()
{
initgraph(800, 600);
setaspectratio(1, -1);
setorigin(400, 300);
setbkcolor(WHITE);
setlinecolor(BLACK);
cleardevice();
struct Rect r = { {}, - 200, 200, 200, 0 };
struct Circle c = { {},0, 0, 100 };
struct Triangle t = { {}, {0, 200}, {-200, 0}, {200, 0} };
InitRect(&r);
InitCircle(&c);
InitTriangle(&t);
struct Shape* arrShape[3] =
{
(struct Shape*)&r,
(struct Shape*)&c,
(struct Shape*)&t
};
for (int i = 0; i < 3; i++)
{
arrShape[i]->draw(arrShape[i]);
}
getchar();
closegraph();
return 0;
}
让我们回顾一下在之前实现多态的步骤:
1.
抽离出各个对象中共有的方法
draw
,将其单独放置在一个对象
Shape
内。
2.
各个对象均继承于
Shape
对象。
3.
将各个子对象中的
draw
方法,设置为各自的实现方法。
4.
声明一个
Shape
对象的指针,并将其赋值为一个子对象的指针。
5.
通过上述对象指针,调用方法共有方法
draw
,执行的是第三步中设置的方法。
注:参考你好编程C语言教程编写,仅供学习参考