用C语言实现C++中类的封装继承多态

1. 基本知识

1.1 结构体

1.2 函数指针

1.3 #ifndef

作用:① 头文件中使用,防止头文件被多重调用;② 作为测试使用,省去注释代码的麻烦;③ 作为不同角色或者场景的判断使用。。
使用:把头文件的内容都放在#ifndef和#endif中

#ifndef <标识>
#define <标识>

......
......

#endif

解释:<标识>在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:stdio.h

#ifndef _STDIO_H_
#define _STDIO_H_

......

#endif

2. C语言实现封装

封装的含义是隐藏内部的行为和信息,使用者能看到对外提供的接口和公开的信息。
实现方法:

2.1 头文件中声明,CPP文件中定义

这样可以隐藏内部信息,因为外部不知道对象所占内存的大小,所以不能静态的创建该类的对象,只能调用类提供的创建函数才能创建。这种方法的缺陷是不支持继承,因为子类中得不到任何关于父类的信息。
//point.h文件

#ifndef POINT_H
#define POINT_H
struct Point;
Point* new_Point(double x,double y); //创建一个新对象
void free_Point(Point* Point_);//释放一个对象
#endif

//point.cpp文件

#include"point.h"
#include <stdlib.h>
#include <stdio.h>
struct Point
{
	double x;
	double y;
};

Point* new_Point(double x, double y)
{
	Point* p = (Point*)malloc(sizeof(Point));//(指针类型)变量名=(指针类型)malloc(n * sizeof(类型)——(指针类型)强转为同变量类型;                                           
	p->x = x;
	p->y = y;
	printf("%s\n","创建新对象");
	return p;
}

void free_Point(Point* Point_)
{
	if (Point_ == NULL)
		return;
	else
	{
		printf("%s\n","释放对象");
		free(Point_);
	}		
}

//主程序中

#include <iostream>
#include"point.h"
#include"stdio.h"
int main()
{
	Point* p1;
	p1 = new_Point(4.5, 5.5);
	free_Point(p1);
	return 0;
}

这样使用point.h 的程序就不知道 Point 的内部结构,实现了数据的封装,外部只能使用声明的两个函数

2.2 私有数据放在不透明的priv变量或者结构体中。

如此操作,只有类的实现代码才知道priv或者结构体的真正定义
//point.h文件中

#ifndef POINT _H

#define POINT_H

typedef struct Point point;

typedef struct PointPrivate pointPrivate;

struct Point
{
struct pointPrivate *pp;
};

double get_x(point* point_);

double get_y(point* point_);

point* new_point(double x_,double y_); //创建新对象

void free_point(point* point_);// 释放对象

#endif
//point.cpp文件中

#include"point.h"
#include"stdlib.h"//malloc的头文件
#include"stdio.h"
struct pointPrivate
{
	double x;
	double y;
};
double get_x(point* point_)
{
	return point_->pp->x;
}
double get_y(point* point_)
{
	return point_->pp->y;
}
point* new_point(double x_, double y_)
{
	pointPrivate* p1 = (pointPrivate*)malloc(sizeof(pointPrivate));
	p1->x = x_;
	p1->y = y_;
	point* p2= (point*)malloc(sizeof(point));
	p2->pp = p1;
	printf("%s\n","构造对象");
	return p2;
}
void free_point(point* point_)
{
	if (point_ == NULL)
		return;
	else
	{
		free(point_);
		printf("%s\n", "释放对象");
	}		
}

//主程序中

#include <iostream>
#include"point.h"
#include"stdio.h"
using namespace std;
int main()
{
	point* p=new_point(4.5, 5.5);
	double a=get_x(p);
	double b = get_y(p);
	printf("%f%s%f\n",a,"  ",b);
	free_point(p);
	return 0;
}

3. C语言实现继承

C语言实现继承是通过结构体嵌套实现的。

4. C语言实现多态

5. 以例说明

5.1 Person.h

typedef struct Person person;
typedef struct Person_act person_act;

//虚函数表结构
struct Person_act
{
	void(*eat)(void*);
	void(*drink)(void*);
};

//基类
struct Person
{
	person_act* per_act;//人的基本行为
};


void person_eat(void* thi);
void person_drink(void* thi);

person* person_init();//构造函数
void person_die(person* per);//析构函数

说明:
C语言实现多态需要借助自定义的虚函数表结构体,结构体中为虚函数指针,表示需要实现多态的虚函数;
结构体Person为定义的基类,并自定义构造函数和析构函数。

5.2 Person.cpp

#include"Person.h"
#include"stdlib.h"
#include"string.h"
#include"stdio.h"

void person_eat(void* thi)
{
	printf("person eat\n");
}

void person_drink(void* thi)
{
	printf("person drink\n");
}

person_act p_act = {person_eat,person_drink};

//构造函数,需要显示调用
person* person_init()
{
	person* temp_per = (person*)malloc(sizeof(person_act));
	temp_per->per_act = &p_act;
	return temp_per;
}

//析构函数,需要显示调用
void person_die(person* per)
{
	if (per == NULL)
		return;
	else
		free(per);
}

说明:
在cpp文件中定义构造函数和析构函数,但是在实际使用时,不像类一样自动调用,需要人为显示调用。
在构造函数中,将实际的函数写入其中,作为此结构体的”虚函数“。

5.3 Worker.h

#pragma once
#include"Person.h"
typedef struct Worker worker;
typedef struct Workerprivate workerprivate;

//派生类
struct Worker
{
	Person person_base;//基类
	workerprivate* worker_info;//派生类信息
};

void worker_eat(void* thi);
void worker_drink(void* thi);
worker* worker_init(const int num);//构造函数
void worker_die(worker* wor);
int get_num(worker* wor);

说明:
定义派生类worker,将基类Person的对象作为结构体的成员实现派生,注意需要将其写在本结构体自身成员的前面——实现继承。
另外,此处将派生类的成员信息重新定义一个结构体,其声明放在.h文件中,定义放在.cpp文件中,实现数据的封装。在.h文中列下接口函数get_num,作为外部函数接口,访问数据。

5.4 Worker.cpp

#include"Worker.h"
#include"stdlib.h"
#include"stdio.h"
#include"string.h"
struct Workerprivate
{
	int num;
};

void worker_eat(void* thi)
{
	printf("worker eat\n");
}

void worker_drink(void* thi)
{
	printf("worker drink\n");
}

person_act w_act = {worker_eat,worker_drink};

worker* worker_init(const int num)
{
	int num_len = sizeof(num);
	worker* temp_wor = (worker*)malloc(sizeof(person)+sizeof(Workerprivate));
	temp_wor->person_base.per_act = &w_act;
	Workerprivate* temp_info = (Workerprivate*)malloc(sizeof(Workerprivate));
	temp_info->num = num;
	temp_wor->worker_info = temp_info;
	return temp_wor;
}

int get_num(worker* wor)
{
	return wor->worker_info->num;
}

说明:
此处定义的结构体:Workerprivate是为了实现数据封装,在此处定义。并在cpp文件中重新定义其对应的虚函数,将其在构造函数中写入worker对应的结构体。
get_num是结构体对外的接口函数,用来访问其”私有数据“——num

5.5 测试程序

#include <iostream>
#include"Person.h"
#include"Worker.h"
#include"stdlib.h"
int main()
{
	//基类测试
	person* person1 = person_init();
	person1->per_act->drink((void*)person1);
	person1->per_act->eat((void*)person1);

	//派生类测试
	worker* worker1 = worker_init(202015);
	//printf("%d\n",worker1->worker_info.num);//直接访问数据不可以,实现了数据封装
	int temp_num = get_num(worker1);
	printf("%d\n", temp_num);
	worker1->person_base.per_act->drink((void*)worker1);
	worker1->person_base.per_act->eat((void*)worker1);

	//多态测试
	person* person2 = person_init();
	worker* worker2= worker_init(202016);
	person2 = (person*)worker2;//基类指针指向派生类对象
	person2->per_act->drink(person2);
	person2->per_act->eat(person2);
}

说明:
基类测试:实例化基类对象,并通过基类对象调用其对应的函数
派生类测试:实例化派生类对象,直接用对象访问其数据成员num不可以,实现了数据封装;但可以通过函数get_num访问其数成员。另外,可以通过派生类对象调用其自身的函数。
多态测试:多态是指函数会根据指针的内容决定调用哪个函数,而不是根据指针的类型决定。通过基类指针指向派生类对象,调用时仍是调用派生类的函数,不是调用基类的函数——即实现了虚函数的功能。

5.6 程序运行结果

在这里插入图片描述

5.7 总结

所有有c++编码规范的公司,都会对指针有大篇幅的约束,因为指针无所不能,刚才也看到了,就连程序的基础运行都依赖指针
虚函数表结构体也是通过指针实现


版权声明:本文为weixin_40332490原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。