C的面向对象

版权声明:如有引用,请附上本文链接 https://blog.csdn.net/weixin_41374099/article/details/89075613

前言

知乎大佬@继续写代码 给我推荐的微信文章《C语言:春节回家过年,我发现只有我没有对象》——码农翻身刘欣 码农翻身
我边看边直呼牛逼!
这段秒啊!
这个竟然这么回事!
牛逼!!

为了熟悉熟悉OOP思想,我把那篇文章说的实现了一下,加入了一点改进。

面向对象三大特性:封装、继承、多态。要想实现面向对象,先把这三个东西实现再说。之后再想如何让它变得简单好用。

(体谅一下博主这么辛苦的编排,麻烦先看一下目录——可能手机端没有目录)

封装(封装方法,不封装变量)

文中用的封装方法的模式和python一样,在写方法的时候需要把对象实例 self 传递进去。

void func(MyClass self,int x,int y);

因为固定了参数 self 的类型,所以也就相当于封装了方法。

至于对于变量的封装 \dots

//Shape.h

#ifndef _SHAPE_H_
#define _SHAPE_H_


#include<stdio.h>
#include<stdlib.h>

struct _Shape {
	int x;
	int y;
};
typedef struct _Shape* Shape;
Shape ShapeCreate(int x, int y);
void ShapeSet(Shape self, int x, int y);
void ShapeMove(Shape self, int dx, int dy);
void ShapeShow(Shape self);

#endif
//Shape.c
#include "Shape.h"

Shape ShapeCreate(int x, int y) {
	Shape s = malloc(sizeof(Shape));
	s->x = x;
	s->y = y;
}
void ShapeSet( Shape self, int x, int y) {
	self->x = x;
	self->y = y;
}
void ShapeMove( Shape self, int dx, int dy) {
	self->x +=dx;
	self->y += dy;
}
void ShapeShow(Shape self) {
	printf("(%3d,%3d)\n", self->x, self->y);
}

//main.c
#include"Shape.h"
int main() {

	//创建Shape对象
	Shape s = ShapeCreate(0, 0);
	ShapeShow(s);

	//设置Shape对象
	ShapeSet(s, 5, 5);
	ShapeShow(s);

	//移动Shape对象
	ShapeMove(s, 10, 10);
	ShapeShow(s);

	system("pause");
	return 0;
}

显示结果:

(  0,  0)
(  5,  5)
( 15, 15)
请按任意键继续. . .

如果我在主函数中,直接访问两个变量这是没有问题的。也就是,这样的封装和python真是很相似呢!不能私有化变量,能封装的只有方法。而达到封装变量的目的,就只有大家心照不宣的通过函数来间接访问。

继承(继承变量和方法)

这个继承有点意思,定义一个新的结构体,把基类的结构体给包括进去。这样就达到了继承变量的目的。

在这里插入图片描述
在定义这样一个结构体时,基类的结构体必须放在头部。这个下面再讲!

//Shape.h
#ifndef _SHAPE_H_
#define _SHAPE_H_


#include<stdio.h>
#include<stdlib.h>

struct _Shape {
	int x;
	int y;
};
struct _Rectangle {
	struct _Shape base;
	int width;
	int height;
};

typedef struct _Shape* Shape;
Shape ShapeCreate(int x, int y);
void ShapeSet(Shape self, int x, int y);
void ShapeMove(Shape self, int dx, int dy);
void ShapeShow(Shape self);


typedef struct _Rectangle* Rectangle;
Rectangle RectangleCreate(int x, int y, int width, int height);
void RectangleShow(Rectangle r);


#endif

//Shape.c
#include "Shape.h"

Shape ShapeCreate(int x, int y) {
	printf("ShapeCreate......\n");
	Shape s = malloc(sizeof(Shape));
	s->x = x;
	s->y = y;
}
void ShapeSet(Shape self, int x, int y) {
	self->x = x;
	self->y = y;
}
void ShapeMove(Shape self, int dx, int dy) {
	printf("Moved....\n");
	self->x += dx;
	self->y += dy;
}
void ShapeShow(Shape self) {
	printf("info > (x = %3d, y = %3d)\n", self->x, self->y);
}

Rectangle RectangleCreate(int x, int y, int width, int height) {
	printf("RectangleCreate.......\n");
	Rectangle r = malloc(sizeof(Rectangle));
	ShapeSet(r, x, y);
	r->width = width;
	r->height = height;
	return r;
}
void RectangleShow(Rectangle r) {

	printf("info > (x = %3d, y = %3d, width = %3d, height = %3d)\n", r->base.x, r->base.y,r->width,r->height);
}


//main.c
#include"Shape.h"
int main() {

	//创建Shape对象
	Shape s = ShapeCreate(0, 0);
	ShapeShow(s);

	//设置Shape对象
	ShapeSet(s, 5, 5);
	ShapeShow(s);

	//移动Shape对象
	ShapeMove(s, 10, 10);
	ShapeShow(s);


	//创建Rectangle对象
	Rectangle r = RectangleCreate(0, 0, 2, 2);
	RectangleShow(r);

	//移动Rectangle对象
	ShapeMove(r, 10, 10);
	RectangleShow(r);

	system("pause");
	return 0;
}


运行结果:

ShapeCreate......
info > (x =   0, y =   0)
info > (x =   5, y =   5)
Moved....
info > (x =  15, y =  15)
RectangleCreate.......
info > (x =   0, y =   0, width =   2, height =   2)
Moved....
info > (x =  10, y =  10, width =   2, height =   2)
请按任意键继续. . .



在RectangleCreate里用了基类的方法,在主函数里也用了ShapeMove。为什么这样包装一下结构体就能除了继承基类的变量,还能继承其方法呢?

在RectangleCreate函数中,r 是Rectangle类型,而使用ShapeSet传递过去的是 _Shape* 类型,所以存在强制类型转换。这个转换就存在使用的隐患。

如果把结构体_Rectangle定义成下面这样:

在这里插入图片描述

显示结果:

ShapeCreate......
info > (x =   0, y =   0)
info > (x =   5, y =   5)
Moved....
info > (x =  15, y =  15)
RectangleCreate.......
info > (x = 1629209996, y = -2147481600, width =   2, height =   2)
Moved....
info > (x = 1629209996, y = -2147481600, width =  12, height =  12)
请按任意键继续. . .

通过调试,发现在RectangleCreate内的ShapeSet运行时,_Rectangle对象实例的前面两个变量被当做了 _Shape 对象的变量。ShapeSet操作的就成了 width 和 height
在这里插入图片描述

原理就是下面这样:类型转换总是取的头部部分,且大小和转换后的类型大小一样
在这里插入图片描述

多态(重载函数)

这个也挺有意思,使用的是函数指针的方法。毕竟函数指针只要返回类型和参数定义好,那么这个函数指针就能和这一类的函数对应起来。
重载函数也是这样,子类重载的函数和基类的函数返回类型和参数定义都一样,所以可以用函数指针来实现。

在这里插入图片描述

//Shape.h
#ifndef _SHAPE_H_
#define _SHAPE_H_


#include<stdio.h>
#include<stdlib.h>

struct _Shape;

//虚函数表
struct _ShapeFuncTable {
	float  (*area)(struct _Shape* self);
	void  (*show)(struct _Shape* self);
};

//实现封装
struct _Shape {
	struct _ShapeFuncTable funcPtr;   //实现多态
	int x;
	int y;
};

//实现继承
//一个子类
struct _Rectangle {
	struct _Shape base;
	int width;
	int height;
};

//又一个子类
struct _Square{
	struct _Shape base;
	int side;
};


typedef struct _Shape* Shape;
//基类的方法
Shape ShapeCreate(int x, int y);
void ShapeSet(Shape self, int x, int y);
void ShapeMove(Shape self, int dx, int dy);
void ShapeShow(Shape self);//实现多态的函数
float ShapeArea(Shape self);//实现多态的函数


typedef struct _Rectangle* Rectangle;
//子类的方法
Rectangle RectangleCreate(int x, int y, int width, int height);
void RectangleShow(Shape self);//子类的重载的函数
float RectangleArea(Shape self);//子类的重载的函数


typedef struct _Square* Square;
//子类的方法
Square SquareCreate(int x, int y, int side);
void SquareShow(Shape self);//子类的重载的函数
float SquareArea(Shape self);//子类的重载的函数




#endif
//Shape.c
#include "Shape.h"

Shape ShapeCreate(int x, int y) {
	printf("ShapeCreate......\n");
	Shape s = (Shape)malloc(sizeof(Shape));
	//基类不需要配置函数指针
	//配置属性
	s->x = x;
	s->y = y;
}
void ShapeSet(Shape self, int x, int y) {
	printf("ShapeSet.......\n");
	self->x = x;
	self->y = y;
}
void ShapeMove(Shape self, int dx, int dy) {
	printf("ShapeMove....\n");
	self->x += dx;
	self->y += dy;
}

//两个多态的基类的函数
void ShapeShow(Shape self) {
	printf("ShapeShow.......\n");
	printf("info > (x = %3d, y = %3d)\n", self->x, self->y);
	if(self->funcPtr.show!=0xfdfdfdfd)
		(self->funcPtr.show)(self);
}
float ShapeArea(Shape self) {
	printf("ShapeArea.......\n");
	if(self->funcPtr.area!= 0xfdfdfdfd)
		return (self->funcPtr.area)(self);//每次调用ShapeArea时,让这个函数指针指向不同的函数表,就实现了多态
	return (self->x * self->y);
}


Rectangle RectangleCreate(int x, int y, int width, int height) {
	printf("RectangleCreate.......\n");
	Rectangle r = (Rectangle)malloc(sizeof(Rectangle));
	//配置函数指针
	r->base.funcPtr.area = RectangleArea;
	r->base.funcPtr.show = RectangleShow;
	//配置Rectangle属性
	ShapeSet(r, x, y);
	r->width = width;
	r->height = height;
	return r;
}
void RectangleShow(Shape self) {
	printf("RectangleShow.......\n");
	Rectangle r = (Rectangle)self;
	printf("info > (x = %3d, y = %3d, width = %3d, height = %3d)\n", r->base.x, r->base.y,r->width,r->height);
}

float RectangleArea(Shape self) {
	printf("RectangleArea.......\n");
	Rectangle r = (Rectangle)self;
	return (r->width * r->height);
}


Square SquareCreate(int x, int y, int side) {
	printf("SquareCreate.......\n");
	Square s= (Square)malloc(sizeof(Square));
	//配置函数指针
	s->base.funcPtr.area = SquareArea;
	s->base.funcPtr.show = SquareShow;
	//配置Square属性
	ShapeSet(s, x, y);
	s->side = side;
	return s;
}
void SquareShow(Shape self) {
	printf("SquareShow.......\n");
	Square s = (Square)self;
	printf("info > (x = %3d, y = %3d, side = %3d)\n", s->base.x, s->base.y, s->side);
}
float SquareArea(Shape  self) {
	printf("SquareArea.......\n");
	Square s = (Square)self;
	return (s -> side * s->side);
}




//main.c
#include"Shape.h"
int main() {

	//创建Shape对象
	Shape s = ShapeCreate(0, 0);
	ShapeShow(s);

	//设置Shape对象
	ShapeSet(s, 5, 5);
	ShapeShow(s);

	//移动Shape对象
	ShapeMove(s, 10, 10);
	ShapeShow(s);


	//创建Rectangle对象
	Rectangle r = RectangleCreate(0, 0, 2, 2);
	RectangleShow(r);

	//移动Rectangle对象
	ShapeMove(r, 10, 10);
	RectangleShow(r);

	printf("Rectangle面积是 > %3f\n", ShapeArea(r));



	//创建Square对象
	Square sq = SquareCreate(0, 0, 55);
	SquareShow(sq);

	printf("Square面积是 > %3f\n",ShapeArea(sq));


	system("pause");
	return 0;
}


运行结果:

ShapeCreate......
ShapeShow.......
info > (x =   0, y =   0)
ShapeSet.......
ShapeShow.......
info > (x =   5, y =   5)
ShapeMove....
ShapeShow.......
info > (x =  15, y =  15)
RectangleCreate.......
ShapeSet.......
RectangleShow.......
info > (x =   0, y =   0, width =   2, height =   2)
ShapeMove....
RectangleShow.......
info > (x =  10, y =  10, width =   2, height =   2)
ShapeArea.......
RectangleArea.......
Rectangle面积是 > 4.000000
SquareCreate.......
ShapeSet.......
SquareShow.......
info > (x =   0, y =   0, side =  55)
ShapeArea.......
SquareArea.......
Square面积是 > 3025.000000
请按任意键继续. . .

能不用指针就不用指针。

当我在_Shape结构体内用 struct _ShapeFuncTable* funcPtr; 编译器就一直提醒我这个指针地址 0xcdcdcdcd ,有写入权限的冲突。但是把星号去掉就没事了。struct _ShapeFuncTable funcPtr

如果是用指针,那么一定要为这个指针申请一块内存。而我之前用了指针,但是没有申请内存,那么这个这个指针的值所代表的单元就不是我能够访问的了。

还有需要注意类型转换的问题。

在使用多态的函数时,比如ShapeArea,传入的子类对象实例会被强制转换成_Shape对象,然后经过函数指针又被转换成子类对象,这样的过程存在内存冲突的风险。

其他修饰

私有化变量

要想把结构体变量都给私有化倒是挺简单。

struct _Shape;
struct _Rectangle;
struct _Square;

把几个结构体的声明放在Shape.h内,把定义放在Shape.c内。
这样就把结构体内所有的变量都给私有化了。

//Shape.h
#ifndef _SHAPE_H_
#define _SHAPE_H_


#include<stdio.h>
#include<stdlib.h>

struct _Shape;
struct _Rectangle;
struct _Square;

//虚函数表
struct _ShapeFuncTable {
	float(*area)(struct _Shape* self);
	void(*show)(struct _Shape* self);
};




typedef struct _Shape* Shape;
//基类的方法
Shape ShapeCreate(char* name, int x, int y);
void ShapeSet(Shape self, int x, int y);  //设置公有变量
void ShapeNameSet(Shape self, char* name);//设置私有变量 name 
void ShapeMove(Shape self, int dx, int dy);
char* ShapeNameGet(const Shape self);   //获取私有变量
void ShapeShow(Shape self);//实现多态的函数
float ShapeArea(Shape self);//实现多态的函数


typedef struct _Rectangle* Rectangle;
//子类的方法
Rectangle RectangleCreate(char* name, int x, int y, int width, int height);
void RectangleShow(Shape self);//子类的重载的函数
float RectangleArea(Shape self);//子类的重载的函数


typedef struct _Square* Square;
//子类的方法
Square SquareCreate(char* name, int x, int y, int side);
void SquareShow(Shape self);//子类的重载的函数
float SquareArea(Shape self);//子类的重载的函数




#endif
//Shape.c
#include "Shape.h"




//实现封装
struct _Shape {
	struct _ShapeFuncTable funcPtr;   //实现多态
	//私有化变量
	char* name;
	//公共变量
	int x;
	int y;
};

//实现继承
//一个子类
struct _Rectangle {
	struct _Shape base;
	int width;
	int height;
};

//又一个子类
struct _Square {
	struct _Shape base;
	int side;
};


Shape ShapeCreate(char* name, int x, int y) {
	printf("ShapeCreate......\n");
	Shape s = (Shape)malloc(sizeof(Shape));
	//配置属性
	s->name = name;
	s->x = x;
	s->y = y;
}
void ShapeSet(Shape self, int x, int y) {
	printf("ShapeSet.......\n");
	self->x = x;
	self->y = y;
}

void ShapeNameSet(Shape self, char* name) {  //设置私有变量 name 
	self->name = name;
}
void ShapeMove(Shape self, int dx, int dy) {
	printf("ShapeMove....\n");
	self->x += dx;
	self->y += dy;
}

char* ShapeNameGet(const Shape self) {   //获取私有变量
	return self->name;
}


//两个多态的基类的函数
void ShapeShow(Shape self) {
	printf("ShapeShow.......\n");
	printf("info > (%10s ,x = %3d, y = %3d)\n", ShapeNameGet(self), self->x, self->y);
	if (self->funcPtr.show != 0xfdfdfdfd)
		(self->funcPtr.show)(self);
}
float ShapeArea(Shape self) {
	printf("ShapeArea.......\n");
	if (self->funcPtr.area != 0xfdfdfdfd)
		return (self->funcPtr.area)(self);//每次调用ShapeArea时,让这个函数指针指向不同的函数表,就实现了多态
	return (self->x * self->y);
}




Rectangle RectangleCreate(char* name, int x, int y, int width, int height) {
	printf("RectangleCreate.......\n");
	Rectangle r = (Rectangle)malloc(sizeof(Rectangle));
	//配置函数指针
	r->base.funcPtr.area = RectangleArea;
	r->base.funcPtr.show = RectangleShow;
	//配置Rectangle属性
	ShapeSet(r, x, y);
	ShapeNameSet(r, name);
	r->width = width;
	r->height = height;
	return r;
}
void RectangleShow(Shape self) {
	printf("RectangleShow.......\n");
	Rectangle r = (Rectangle)self;
	printf("info > (%10s ,x = %3d, y = %3d, width = %3d, height = %3d)\n", ShapeNameGet(r), r->base.x, r->base.y, r->width, r->height);
}

float RectangleArea(Shape self) {
	printf("RectangleArea.......\n");
	Rectangle r = (Rectangle)self;
	return (r->width * r->height);
}




Square SquareCreate(char* name, int x, int y, int side) {
	printf("SquareCreate.......\n");
	Square s = (Square)malloc(sizeof(Square));
	//配置函数指针
	s->base.funcPtr.area = SquareArea;
	s->base.funcPtr.show = SquareShow;
	//配置Square属性
	ShapeSet(s, x, y);
	ShapeNameSet(s, name);
	s->side = side;
	return s;
}
void SquareShow(Shape self) {
	printf("SquareShow.......\n");
	Square s = (Square)self;
	printf("info > (%10s ,x = %3d, y = %3d, side = %3d)\n", ShapeNameGet(s), s->base.x, s->base.y, s->side);
}
float SquareArea(Shape  self) {
	printf("SquareArea.......\n");
	Square s = (Square)self;
	return (s->side * s->side);
}



//main.c
#include"Shape.h"
int main() {

	//创建Shape对象
	Shape s = ShapeCreate("<name,\"Shape\">", 0, 0);
//	printf("%d,%d\n", s->x, s->y); //非法访问
	ShapeShow(s);

	//设置Shape对象
	ShapeSet(s, 5, 5);
	ShapeNameSet(s, "<name,\"_S_h_a_p_e_\">");
	ShapeShow(s);

	//移动Shape对象
	ShapeMove(s, 10, 10);
	ShapeShow(s);


	//创建Rectangle对象
	Rectangle r = RectangleCreate("<name,\"Rectangle\">", 0, 0, 2, 2);
	RectangleShow(r);

	//移动Rectangle对象
	ShapeMove(r, 10, 10);
	RectangleShow(r);

	printf("Rectangle面积是 > %3f\n", ShapeArea(r));



	//创建Square对象
	Square sq = SquareCreate("<name,\"Square\">", 0, 0, 55);
	SquareShow(sq);

	printf("Square面积是 > %3f\n", ShapeArea(sq));


	system("pause");
	return 0;
}

显示结果:

ShapeCreate......
ShapeShow.......
info > (<name,"Shape"> ,x = -2013265920, y =   8)
ShapeSet.......
ShapeShow.......
info > (<name,"_S_h_a_p_e_"> ,x =   5, y =   5)
ShapeMove....
ShapeShow.......
info > (<name,"_S_h_a_p_e_"> ,x =  15, y =  15)
RectangleCreate.......
ShapeSet.......
RectangleShow.......
info > (<name,"Rectangle"> ,x =   0, y =   0, width =   2, height =   2)
ShapeMove....
RectangleShow.......
info > (<name,"Rectangle"> ,x =  10, y =  10, width =   2, height =   2)
ShapeArea.......
RectangleArea.......
Rectangle面积是 > 4.000000
SquareCreate.......
ShapeSet.......
SquareShow.......
info > (<name,"Square"> ,x =   0, y =   0, side =  55)
ShapeArea.......
SquareArea.......
Square面积是 > 3025.000000
请按任意键继续. . .

简单的把声明和定义分开就私有化了所有变量,实现封装似乎也不过如此。

但是,这样就满足要求了吗?
在这里插入图片描述

但是这样就把结构体内所有的变量都封装了,私有化所有的变量不是我所期望的!我想要的是访问x,y这样的公有变量没有问题,而访问name这样的私有变量才报错。

那么,如何设计这样一个结构体,能既有私有的(定义在Shape.c中的结构体)也有公共的(定义在Shape.h的结构体)这两个部分呢?

&ThickSpace; \;
&ThickSpace; \;
&ThickSpace; \;
&ThickSpace; \;
&ThickSpace; \;


封装变量(同时具有私有化、公有化)

为了访问公有化变量,这个“总”的结构体一定要定义在Shape.h内,然后私有化变量的结构体定义在Shape.c内,所以只需要在_Shape结构体内定义一下私有的结构体就好。

说来也奇怪,如果像下面这样定义,那么会报错:

错误(活动) E0070 不允许使用不完整的类型 HHH E:\Can\HHH\HHH\Shape.h 27

在这里插入图片描述

此时27行这一直报错,使用了未定义的结构体。但是加了个星号后,又没错了???
是否可以每次遇到未定义结构体的时候就试试加星号呢?

这个问题和指针变量、一般的变量定义有关!!!

本来想把问题留在这里等大佬出来解释,但一想到没有人看,便觉得还是靠自己吧! 定义int* a=0和int b,这两个变量大小都是4个字节,都有地址和值。 而变量 a 因为没有申请空间,所以无法读取内存。所以我猜想,指针变量无需完整的类型定义,只有当为这个指针变量申请了内存后,才考虑其类型是否完整。

//Shape.h
#ifndef _SHAPE_H_
#define _SHAPE_H_


#include<stdio.h>
#include<stdlib.h>

struct _Shape;
struct _Rectangle;
struct _Square;
struct _PrivateVariations;



//虚函数表
struct _ShapeFuncTable {
	float(*area)(struct _Shape* self);
	void(*show)(struct _Shape* self);
};



//实现封装
struct _Shape {
	struct _ShapeFuncTable funcPtr;   //实现多态
	struct _PrivateVariations* pVar;//私有变量
	//公有变量
	int x;
	int y;
};

//实现继承
//一个子类
struct _Rectangle {
	struct _Shape base;
	int width;
	int height;
};

//又一个子类
struct _Square {
	struct _Shape base;
	int side;
};


typedef struct _Shape* Shape;
//基类的方法
Shape ShapeCreate(char* name, int x, int y);
void ShapeSet(Shape self, int x, int y);  //设置公有变量
void ShapeNameSet(Shape self, char* name);//设置私有变量 name 
void ShapeMove(Shape self, int dx, int dy);
char* ShapeNameGet(const Shape self);   //获取私有变量
void ShapeShow(Shape self);//实现多态的函数
float ShapeArea(Shape self);//实现多态的函数


typedef struct _Rectangle* Rectangle;
//子类的方法
Rectangle RectangleCreate(char* name, int x, int y, int width, int height);
void RectangleShow(Shape self);//子类的重载的函数
float RectangleArea(Shape self);//子类的重载的函数


typedef struct _Square* Square;
//子类的方法
Square SquareCreate(char* name, int x, int y, int side);
void SquareShow(Shape self);//子类的重载的函数
float SquareArea(Shape self);//子类的重载的函数




#endif
//Shape.c
#include "Shape.h"



struct _PrivateVariations {
	char* name;
};
typedef struct _PrivateVariations* PVar;


Shape ShapeCreate(char* name, int x, int y) {
	printf("ShapeCreate......\n");
	Shape s = (Shape)malloc(sizeof(Shape)); //为_Shape*分配内存
	s->pVar = (PVar)malloc(sizeof(PVar));//为_PrivateVariations*分配内存
	//配置属性
	s->pVar->name = name;
	s->x = x;
	s->y = y;
	return s;
}
void ShapeSet(Shape self, int x, int y) {
	printf("ShapeSet.......\n");
	self->x = x;
	self->y = y;
}

void ShapeNameSet(Shape self, char* name) {  //设置私有变量 name 
	self->pVar->name = name;
}
void ShapeMove(Shape self, int dx, int dy) {
	printf("ShapeMove....\n");
	self->x += dx;
	self->y += dy;
}

char* ShapeNameGet(const Shape self) {   //获取私有变量
	return self->pVar->name;
	//return NULL;
}


//两个多态的基类的函数
void ShapeShow(Shape self) {
	printf("ShapeShow.......\n");
	printf("info > (%10s ,x = %3d, y = %3d)\n", ShapeNameGet(self), self->x, self->y);
	if (self->funcPtr.show != 0xfdfdfdfd)
		(self->funcPtr.show)(self);
}
float ShapeArea(Shape self) {
	printf("ShapeArea.......\n");
	if (self->funcPtr.area != 0xfdfdfdfd)
		return (self->funcPtr.area)(self);//每次调用ShapeArea时,让这个函数指针指向不同的函数表,就实现了多态
	return (self->x * self->y);
}




Rectangle RectangleCreate(char* name, int x, int y, int width, int height) {
	printf("RectangleCreate.......\n");
	Rectangle r = (Rectangle)malloc(sizeof(Rectangle));//为_Rectangle*分配内存
	r->base.pVar = (PVar)malloc(sizeof(PVar));//为_PrivateVariations*分配内存
	//配置函数指针
	r->base.funcPtr.area = RectangleArea;
	r->base.funcPtr.show = RectangleShow;
	//配置Rectangle属性
	ShapeSet(r, x, y);
	ShapeNameSet(r, name);
	r->width = width;
	r->height = height;
	return r;
}
void RectangleShow(Shape self) {
	printf("RectangleShow.......\n");
	Rectangle r = (Rectangle)self;
	printf("info > (%10s ,x = %3d, y = %3d, width = %3d, height = %3d)\n", ShapeNameGet(r), r->base.x, r->base.y, r->width, r->height);
}

float RectangleArea(Shape self) {
	printf("RectangleArea.......\n");
	Rectangle r = (Rectangle)self;
	return (r->width * r->height);
}




Square SquareCreate(char* name, int x, int y, int side) {
	printf("SquareCreate.......\n");
	
	Square s = (Square)malloc(sizeof(Square));//为_Square*分配内存
	s->base.pVar = (PVar)malloc(sizeof(PVar));//为_PrivateVariations*分配内存
	//配置函数指针
	s->base.funcPtr.area = SquareArea;
	s->base.funcPtr.show = SquareShow;
	//配置Square属性
	ShapeSet(s, x, y);
	ShapeNameSet(s, name);
	s->side = side;
	return s;
}
void SquareShow(Shape self) {
	printf("SquareShow.......\n");
	Square s = (Square)self;
	printf("info > (%10s ,x = %3d, y = %3d, side = %3d)\n", ShapeNameGet(s), s->base.x, s->base.y, s->side);
}
float SquareArea(Shape  self) {
	printf("SquareArea.......\n");
	Square s = (Square)self;
	return (s->side * s->side);
}



main.c没更改,显示结果也一致。

为了验证是否实现了私有化和公有化变量共存的情况,可以在主函数内访问几个变量试试。

在这里插入图片描述


扩大虚函数表

仅仅是两个area,show这种固定的函数还是不行,为了之后的可拓展性,我需要定义参数可变的函数,并且扩大虚函数表,返回类型也需要多种多样!!!

为了不影响前面的代码,避免不小心的错误,之前的两个旧的函数指针就留着。
建立下面这样一张表后,随便实现几个函数看看!


//虚函数表
struct _ShapeFuncTable {
	//旧表
	float(*area)(struct _Shape* self);
	void(*show)(struct _Shape* self);

	//待扩展的表
	int(*MyFuncPtr_INT)(struct _Shape* self, ...);
	float(*MyFuncPtr_FLOAT)(struct _Shape* self, ...);
	double(*MyFuncPtr_DOUBLE)(struct _Shape* self,  ...);
	void(*MyFuncPtr_VOID)(struct _Shape* self,...);
	char*(*MyFuncPtr_STR)(struct _Shape* self,  ...);

};
  • 虽说表的函数指针参数是可变的,但是实现多态的参数是确定的,重载的函数参数设置也与被重载的相同。表的目的在于之后写不同的重载函数都查表就可以,无需更改表。

实现下析构

前面的代码,如果是直接用CTRL+F5执行是没有问题的。但是直接调试在这里插入图片描述就会出错。问题在于系统自动释放内存的过程中对于前面我申请的内存其地址更改了(大概)导致了访问冲突,不找到这个问题,那么析构也没办法写。

为了查看是否是地址的问题,需要在申请内存的位置后和最后释放的位置(写个释放Shape内存的函数ShapeDelete实验一下)前把地址打印出来。

在这里插入图片描述

地址显示内存并未移动,但是一到释放内存(Shape、pVar)的时候就报错。在这里插入图片描述

这个问题我就又放在这了!什么时候想起来再说。


猜你喜欢

转载自blog.csdn.net/weixin_41374099/article/details/89075613