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