当前位置:   article > 正文

C语言中的封装、多态、继承--------打破你对面向对象的观念-------关于结构体、对象指针、函数指针_c语言把对结构体成员变量的操作封装

c语言把对结构体成员变量的操作封装

前言

Objective-C是C语言的超集,具有面向对象的特性,也许也是通过函数指针什么的实现的!

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

绑定方法和对象

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

void func(OBJ* self,int x,int y);
  • 1

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

至于对于变量的封装 … \dots

//Shape.h
#ifndef _SHAPE_H_
#define _SHAPE_H_
#include<stdio.h>
#include<stdlib.h>


typedef struct _Shape {
	int x;
	int y;
}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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
//Shape.c
#include "Shape.h"

Shape* ShapeCreate(int x, int y) {
	Shape* s = (Shape*)malloc(sizeof(Shape));
	s->x = x;
	s->y = y;
	return s;
}
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);
}


  • 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"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;
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

显示结果:

(  0,  0)
(  5,  5)
( 15, 15)
请按任意键继续. . .
  • 1
  • 2
  • 3
  • 4

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

继承(继承变量)

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

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

//Shape.h
#ifndef _SHAPE_H_
#define _SHAPE_H_
#include<stdio.h>
#include<stdlib.h>

typedef struct _Shape {
	int x;
	int y;
}Shape;

typedef struct _Rectangle {
	struct _Shape base;
	int width;
	int height;
}Rectangle;


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);



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

#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
//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;
	return s;
}
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 = (Rectangle*)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);
}

  • 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
//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;
}


  • 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

运行结果:

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)
请按任意键继续. . .



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在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)
请按任意键继续. . .

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

通过调试,发现在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);
};

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

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

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



//基类的方法
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);//实现多态的函数



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



//子类的方法
Square* SquareCreate(int x, int y, int side);
void SquareShow(Shape* self);//子类的重载的函数
float SquareArea(Shape* 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
  • 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
//Shape.c
#include "Shape.h"

Shape* ShapeCreate(int x, int y) {
	printf("ShapeCreate......\n");
	Shape* s = (Shape*)malloc(sizeof(Shape));
	//基类不需要配置函数指针
	s->funcPtr.area = NULL;
	s->funcPtr.show = NULL;
	//配置属性
	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 ShapeMove(Shape* self, int dx, int dy) {
	printf("ShapeMove....>>>\n");
	self->x += dx;
	self->y += dy;
}

//两个多态的基类的函数
void ShapeShow(Shape* self) {	
	if (self->funcPtr.show != NULL)
		self->funcPtr.show(self);
	else {
		printf("ShapeShow.......\n");
		printf("info > (x = %3d, y = %3d)\n", self->x, self->y);
	}
}
float ShapeArea(Shape* self) {
	if (self->funcPtr.area != NULL)
		return self->funcPtr.area(self);
	else {
		printf("ShapeArea.......\n");
		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);
}





  • 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
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
//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);
	ShapeShow(r);//和上面一条效果一样
	r->base.funcPtr.show(r);//和上面一条效果一样

	//移动Rectangle对象
	ShapeMove(r, 10, 10);
	RectangleShow(r);
	ShapeShow(r);//和上面一条效果一样

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



	//创建Square对象
	Square* sq = SquareCreate(0, 0, 55);
	SquareShow(sq);
	ShapeShow(sq);//和上面一条效果一样

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


	system("pause");
	return 0;
}




  • 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

运行结果:

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)
RectangleShow.......
info > (x =   0, y =   0, width =   2, height =   2)
ShapeMove....>>>
RectangleShow.......
info > (x =  10, y =  10, width =   2, height =   2)
RectangleShow.......
info > (x =  10, y =  10, width =   2, height =   2)
RectangleArea.......
Rectangle面积是 > 4.000000
SquareCreate.......
ShapeSet.......+++
SquareShow.......
info > (x =   0, y =   0, side =  55)
SquareShow.......
info > (x =   0, y =   0, side =  55)
SquareArea.......
Square面积是 > 3025.000000
请按任意键继续. . .

  • 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

注意为指针变量分配内存

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

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

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

私有化变量

要想把结构体变量都给私有化倒是挺简单!!!
把几个结构体的声明放在头文件内,把定义放在源文件内
这样就把结构体内所有的变量都给私有化了。

//Shape.h
#ifndef _SHAPE_H_
#define _SHAPE_H_


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

typedef struct _Shape Shape; 
typedef struct _Rectangle  Rectangle;
typedef struct _Square Square;

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




//基类的方法
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(Shape* self);   //获取私有变量
void ShapeShow(Shape* self);//实现多态的函数
float ShapeArea(Shape* self);//实现多态的函数



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



//子类的方法
Square* SquareCreate(char* name, int x, int y, int side);
void SquareShow(Shape* self);//子类的重载的函数
float SquareArea(Shape* 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
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
//Shape.c
#include "Shape.h"




//实现封装
typedef struct _Shape {
	struct _ShapeFuncTable funcPtr;   //实现多态
	//变量都是私有的
	char* name;
	int x;
	int y;
}Shape;



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



//又一个子类
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->funcPtr.area = NULL;
	s->funcPtr.show = NULL;
	s->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->name = name;
}
void ShapeMove(Shape* self, int dx, int dy) {
	printf("ShapeMove....\n");
	self->x += dx;
	self->y += dy;
}

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


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

  • 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
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
//main.c
#include"Shape.h"
int main() {

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

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

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


	//创建Rectangle对象
	Rectangle* r = RectangleCreate("Rectangle", 0, 0, 2, 2);
	ShapeShow(r);

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

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



	//创建Square对象
	Square* sq = SquareCreate("Square", 0, 0, 55);
	ShapeShow(sq);

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


	system("pause");
	return 0;
}

  • 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

显示结果:

ShapeCreate......)))
ShapeShow.......
info > (     Shape ,x =   0, y =   0)
ShapeSet.......+++
ShapeShow.......
info > (  NewShape ,x =   5, y =   5)
ShapeMove....
ShapeShow.......
info > (  NewShape ,x =  15, y =  15)
RectangleCreate.......)))
ShapeSet.......+++
RectangleShow.......
info > ( Rectangle ,x =   0, y =   0, width =   2, height =   2)
ShapeMove....
RectangleShow.......
info > ( Rectangle ,x =  10, y =  10, width =   2, height =   2)
RectangleArea.......
Rectangle面积是 > 4.000000
SquareCreate.......)))
ShapeSet.......+++
SquareShow.......
info > (    Square ,x =   0, y =   0, side =  55)
SquareArea.......
Square面积是 > 3025.000000
请按任意键继续. . .
  • 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

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

但是,这样就满足要求了吗?

在这里插入图片描述

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

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

   \;
   \;
   \;
   \;
   \;


封装变量Private&Public

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

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

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

在这里插入图片描述

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

我猜猜,凡是指针类型的变量,编译器就不管其类型是否之前声明过!!!

//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);
};



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

}Shape;

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

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




//基类的方法
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(Shape* self);   //获取私有变量
void ShapeShow(Shape* self);//实现多态的函数
float ShapeArea(Shape* self);//实现多态的函数



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



//子类的方法
Square* SquareCreate(char* name, int x, int y, int side);
void SquareShow(Shape* self);//子类的重载的函数
float SquareArea(Shape* 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
  • 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
//Shape.c
#include "Shape.h"



//私有变量
typedef struct _PrivateVariable {
	char* name;
}PVar;





Shape* ShapeCreate(char* name, int x, int y) {
	printf("ShapeCreate......)))\n");
	Shape* s = (Shape*)malloc(sizeof(Shape));
	//配置属性
	s->funcPtr.area = NULL;
	s->funcPtr.show = NULL;
	ShapeNameSet(s, 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 = (PVar*)malloc(sizeof(PVar));
	self->pVar->name = name;
}
void ShapeMove(Shape* self, int dx, int dy) {
	printf("ShapeMove....\n");
	self->x += dx;
	self->y += dy;
}

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


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

  • 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
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118

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

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

在这里插入图片描述


私有化函数

既然前面私有化变量,那么私有化函数也是同样的道理!

//Shape.h
...
//实现封装
typedef struct _Shape {
	//私有函数
	struct _PrivateFuncTable*  pFuncPtr;
	//公有函数
	struct _ShapeFuncTable funcPtr;   
	
   //私有变量
	struct _PrivateVariable* pVar;
	//公有变量
	int x;
	int y;

}Shape;
...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
//Shape.c
...

//私有化函数
typedef struct _PrivateFuncTable {
	void(*set)(struct _Shape* self);
}PFuncT;



//私有函数
void ShapeAddX(Shape* self) {
	printf("ShapeSet.......ooo\n");
	self->x+=100;
}

//初始化私有函数
#define PRIVATE_FUNC_INIT(func) \
{\
s->pFuncPtr = (PFuncT*)malloc(sizeof(PFuncT));\
s->pFuncPtr->set = func;\
}

Shape* ShapeCreate(char* name, int x, int y) {
	printf("ShapeCreate......)))\n");
	Shape* s = (Shape*)malloc(sizeof(Shape));
	//配置属性
	PRIVATE_FUNC_INIT(ShapeAddX);
	
	s->funcPtr.area = NULL;
	s->funcPtr.show = NULL;

	ShapeNameSet(s, name);
	s->x = x;
	s->y = y;
	return s;
}


//提供给外面使用私有函数接口
void Shape_UsePrivateFunc(Shape* self) {
	(self->pFuncPtr->set)(self);
}
...
  • 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

扩大虚函数表

仅仅是两个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,  ...);

};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 虽说表的函数指针参数是可变的,但是实现多态的参数是确定的,重载的函数参数设置也与被重载的相同。表的目的在于之后写不同的重载函数都查表就可以,无需更改表

析构

//Shape.h
#define FREE_OBJ(obj)  free(obj);obj=NULL
  • 1
  • 2

参考:《C语言:春节回家过年,我发现只有我没有对象》——码农翻身刘欣 码农翻身

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/392638
推荐阅读
相关标签
  

闽ICP备14008679号