C模拟面向对象工具库

  • Post author:
  • Post category:其他




一.项目简介(Program info)

项目地址

工程仓库


本项目的初衷在于,使用

java和c++进行了许多的面向对象编程

之后。

由于电子专业原因。用回C时,感觉在编程的过程中不再那么规范了。

于是产生了

使用 “函数指针”

这一知识点写一个

模拟面向对象

的容器库。

这样在调用的时候就可以利用类似

结构体.函数

这一种

面向对象的方法操作

省的每次再去头文件或者其他地方copy函数名字。



让代码的内聚性

更加高


头文件有很多的静态函数 但是静态不用管其实现,只需要调用结构体内部的函数指针即可使用

本人几乎没学过算法,所以源码里面的一些函数部分如果大家有更好的解决办法欢迎提出,

代码里肯定也存在一些不符合设计原则的地方。有时间会慢慢重构改善的



当前存在问题

1:如何写出this指针,不然每次传参都得把自己传进去,太麻烦了

2:如何实现泛型

3:迭代器设计模式



二.集合(Collector)



1.Vector

是一个

连续空间的存储结构

名字由来于C++的stl容器库中的vector,有该有的功能,也有添加的功能,当然也有没实现的功能


使用方法:用New_Vector()这个接口函数

获取vector的指针对象即可。

由于篇幅原因,测试代码和部分注释就只放了一小部分

Vector中的成员介绍
data 真实存放的数据
capacity 当前Vetor的容量
size 当前Vector的数据个数
remove 指定索引弹出
lpop 左弹出
rpop 右弹出
insert 指定位置插入
emplace 指定位置插入(覆盖式插入)
lpush 左插入
rpush 右插入
at 返回当前索引下标的数据地址
isEmpty 是否为空
clear 清除
destory 销毁
resize 重新指定大小
swap 交换两元素下标
toString 打印Vector里的data地址
front 返回第一个元素的索引
back 返回最后一个元素的索引
shrink_to_fit 自动缩小到对应大小
reverse 反转
#define VECTOR_DEFAULT_CAPCITY 10
//test
typedef struct student {
	char* name;
}Student;

/// <summary>
/// 向量: 连续的存储结构
/// </summary>
typedef struct vector {
	void** data;
	int capacity;
	int size;
	
	void* (*remove)(struct vector*, int index);
	void* (*lpop)(struct vector*);
	void* (*rpop)(struct vector*);
	//...省略后续
}Vector;

//-----------------接口---------------------
extern Vector* New_Vector();

测试代码

void test_rpop()
{
	char* name[12] = { "AA","BB","CC", "DD", "EE", "FF", "GG", "HH", "II","JJ", "KK", "LL" };
    //调用接口
	Vector* vector = New_Vector();
	for (size_t i = 0; i < 12; i++)
	{
        //这里的new是宏定义  #define new(T) calloc(1, sizeof(T))
		Student* stu = new(Student);
		stu->name = name[i];
        //传参传入自己,因为我暂时写不出this指针
		vector->rpush(vector, stu);
		stu = NULL;
		free(stu);
	}
	for (size_t i = 0; i < 12; i++)
	{
		Student* stu = vector->rpop(vector);
        //宏定义 防止空指针导致程序崩溃
		__NOT_NULL(stu, printf("name:%s\n", stu->name););
	}
}



2.Queue

队列Queue是一种先进先出的数据结构,利用成员变量继承自Vector基类

由于Vector是连续的,所以先进先出时,弹出时相当于所有数据都要遍历一次

未来实现了链表的时候,会用链表优化


使用方法:调用New_Queue()获取一个Queue变量即可

Queue中的成员介绍
push 插入数据
pop 弹出数据
front 返回第一个元素的索引
back 返回最后一个元素的索引
empty 是否为空
#pragma once
#include "vector.h"
//模拟C++和javad的Queue
//结构:利用成员变量继承自Vector,做出了面向对象部分的继承效果。
//使用方法:调用 New_Queue() 接口实例化对象,然后按需调用结构体内部的函数就行
//底下面向过程的函数是静态的所以无法调用哦 
typedef struct queue {
	int size;
	Vector* vector;

	void (*push)(struct stack*, void* data);
	void* (*pop)(struct stack*);
	void* (*front)(struct stack*);
	void* (*back)(struct stack*);

	//...省略后续
}Queue;
//接口
extern Queue* New_Queue();

测试代码

void queue_test()
{
	Queue* queue = New_Queue();
	char* name[12] = { "AA","BB","CC", "DD", "EE", "FF", "GG", "HH", "II","JJ", "KK", "LL" };
	if (queue->empty(queue)) {
		printf("Now is empty\n");
	}

	for (size_t i = 0; i < 12; i++)
	{
		Student* stu = new(Student);
		stu->name = name[i];
		stu->age = 1000000000;
		stu->hobby = "learn";
		queue->push(queue, stu);
	}
	queue->toString(queue);
	for (size_t i = 0; i < 12; i++)
	{
		Student* stu = queue->pop(queue);
		printf("%s  %d    %s\n", stu->name, stu->age, stu->hobby);
	}
	printf("\n");
}



3.Stack

栈Stack是一种先进后出的数据结构,利用成员变量方法继承自Vector基类


使用方法:调用 New_Stack();获取一个栈变量

Stack中的成员介绍
push 插入数据入栈
pop 弹出栈顶数据
top 返回第一个元素的索引
empty 是否为空
#pragma once
#include "vector.h"
//模拟C++和javad的Stack
//结构:利用成员变量继承自Vector。以及对应所需的函数
//使用方法:调用 New_Stack() 接口实例化对象,然后按需调用结构体内部的函数就行
//底下面向过程的函数是静态的所以无法调用哦 
typedef struct stack {
	int size;
	Vector* vector;

	void (*push)(struct stack*,void* data);
	void* (*pop)(struct stack*);
	void* (*top)(struct stack*);

	//...省略后续
}Stack;
//接口
Stack* New_Stack();

测试代码

void stack_test()
{
	Stack* stack = New_Stack();
	char* name[12] = { "AA","BB","CC", "DD", "EE", "FF", "GG", "HH", "II","JJ", "KK", "LL" };
	if (stack->empty(stack)) {
		printf("Now is empty\n");
	}
	
	for (size_t i = 0; i < 12; i++)
	{
		Student* stu = new(Student);
		stu->name = name[i];
		stu->age = 1000000000;
		stu->hobby = "learn";
		stack->push(stack, stu);
	}
	stack->toString(stack);
	for (size_t i = 0; i < 12; i++)
	{
		Student* stu = stack->pop(stack);
		printf("%s  %d    %s\n", stu->name,stu->age,stu->hobby);
	}
	printf("\n");
}



4.List


List是一个双向链表

List中的成员介绍
addFirst 向前插入一个数据
addLast 向后插入一个数据
insert 向任意位置插入一个数据
removeFirst 移除最前面的一个元素
removeLast 移除最后一个元素
deleteNode 删除任意位置的元素
isEmpty 是否为空
destory 销毁
printList 打印
int main()
{
	Dlist* listNode = New_DList();
	listNode->addLast(listNode, 1);
	listNode->addLast(listNode, 2);

	int a = listNode->removeFirst(listNode);
	int b =listNode->removeLast(listNode);
	printf("%d %d\n",a,b);
	printf("%d\n", listNode->size);
	printf("%d\n", listNode->isEmpty(listNode));
	listNode->printList(listNode);
}



5.DStack


Dstack是一个链表实现的栈(前面那个是基于数组)

List中的成员介绍
addFirst 向前插入一个数据
addLast 向后插入一个数据
insert 向任意位置插入一个数据
removeFirst 移除最前面的一个元素
removeLast 移除最后一个元素
deleteNode 删除任意位置的元素
isEmpty 是否为空
destory 销毁
printList 打印
int main()
{
	DStack* dstack = New_DStack();
	dstack->push(dstack, 1);
	dstack->push(dstack, 2);
	dstack->push(dstack, 3);
	dstack->push(dstack, 4);
	dstack->push(dstack, 5);

	int a = dstack->pop(dstack);
	int b = dstack->pop(dstack);
	printf("%d %d\n", a, b);
	printf("%d\n", dstack->size);
	printf("%d\n", dstack->isEmpty(dstack));
	dstack->printStack(dstack);
}



6.Deque


Deque是一个链表实现的队列(前面那个Queue是基于数组)

使用方法和成员变量与DStack完全一样 这里不做展示;



三.字符串(String)

可以理解为是一种加强的char*

以前使用char*时 如果涉及数据处理什么的,我们都需要去调用很多str系列的函数方法,例如strcpy,strcmp,strstr之类的

于是我

封装了一个String结构体


内置了很多的处理函数

,现在我们只需要使用一个String变量

即可方便的对我们的字符串数据进行处理


例如我们要转换大写

String* str = New_String("Jack");
str->upper(str);//调用upper即可进行大写转化

例如我们要截取串

String* str = New_String("BirthDay");
String* str2 = str->substring(str, 0 , 4);

例如我们要看是否包含子串

String* str = New_String("BirthDay");
if (str->contains(str, "Bir")) {
	puts("has the Bir");
}

下面给上函数

String中的成员介绍
data 保存的真实数据
size 数据长度
empty 串是否为空
upper 串转大写
lower 串转小写
substring 切割串,返回新串地址
copy 复制原串到目标串
equal 两串是否相等
equalIgnoreCase 两串是否相等(忽略大小写)
contains 是否包含子串
append 追加(链式)
charAt 返回指定索引的字符
#pragma once
#define _CRT_NONSTDC_NO_DEPRECATE
#define _CRT_SECURE_NO_WARNINGS
#include "common.h"
//模拟C++和java的String库
//使用方法:用两个接口函数进行初始化,取决于你要无参还是有参
//有需要时调用结构体内部的函数即可,底下的静态函数你用不了,也不要用
//做成静态是因为我的初衷就是要模拟面向对象,所以会去掉部分面向过程的函数
typedef struct string String;
struct string
{	
	//data
	int size;
	char* data;
	//function
	bool (*empty)(String*);
	String* (*upper)(String*);
	String* (*lower)(String*);
	String* (*substring)(String* str,int start, int end);
	//...省略后续

};
//接口
extern String* New_String_NoArg(); 
extern String* New_String(char* data);

测试代码

void string_test1() {
	String* str = New_String("Jack");
	printf("%s size:%d\n", str->data, str->size);
	str->upper(str);
	printf("Upper:%s size:%d\n", str->data, str->size);
	str->lower(str);
	printf("Lower:%s size:%d\n", str->data, str->size);
	String* str2 = New_String("");
	if (str2->empty(str2)) {
		printf("Str2 is empty\n");
	}
}
void string_test2()
{
	String* str = New_String("BirthDay");
	String* str2 = str->substring(str, 0 , 4);
	printf("substring:%s\n", str2->data);
	str->copy(str, str2);
	printf("copy:%s\n", str2->data);
	if (str->equal(str, str2)) {
		puts("now is equal");
	}
	str->append(str," to")->append(str," you");
	printf("append:%s\n", str->data);
	printf("charAt:%c\n", str->charAt(str,0));
	if (str->contains(str, "Bir")) {
		puts("has the Bir");
	}
}



四.工具库(Tool Lib)



1.IntTool

一个专门服务于各种Int事项的工具类

调用方法只需要,先调用IntToolFactory()进行一次装配

然后使用全局变量 IntTool.函数()即可。

Int_Tool成员介绍
MAX_VALUE int最大值
MIN_VALUE int最小值
Max 返回最大数
Min 返回最小数
toBinaryString 返回2进制string
toOctalString 返回8进制string
toHexString 返回16进制string
toDecString 返回10进制string
toBinaryChar 返回2进制char*
toOctalChar 返回8进制char*
toHexChar 返回16进制char*
toDecChar 返回10进制char*
parseDecInt 10进制char*转int
parseBinInt 2进制char*转int
arrayMax 数组最大值
arrayMin 数组最小值
arrayAverage 数组平均值
arraySum 数组总和
sortByASEC 升序排序数组
sortByDESC 降序排序数组
printArr 打印数组
#pragma once
#include "common.h"
#include "a_string.h"
#include "Interger.h"

/**
* 标题:单例模式设计的int工具类
* 使用方法:调用IntToolFactory();进行装配,并调用全局变量IntTool结构体里的函数即可
*/
typedef struct intTool Int_Tool;
struct intTool {
	int MAX_VALUE;
	int MIN_VALUE;
	int (*Max)(int a, int b);
	int (*Min)(int a, int b);
	int (*sum)(int a, int b);

	//省略后续...
};
//-----Interface-----
Int_Tool IntTool;
//-----Factory-----
extern void IntToolFactory();

部分测试代码

	printf("MAX_VALUE:%d  MIN_VALUE:%d\n", IntTool.MAX_VALUE, IntTool.MIN_VALUE);
	printf("parseDecInt:%d\n", IntTool.parseDecInt("123321"));
	printf("Char* Binary:%s\n", IntTool.toBinaryChar(INT_MAX));
	int a[] = { 1,2,5,4,3 };
	printf("intArrMax:%d\n", IntTool.arrayMax(a, ARR_SIZE(a)));
	IntTool.sortByASEC(a, ARR_SIZE(a));
	//底下这个到时候提供一个数组遍历吧
	printf("SortASEC:%d %d %d %d %d\n", a[0], a[1], a[2], a[3], a[4]);



2.DoubleTool

一个专门服务于各种Double事项的工具类

调用方法只需要,先调用DoubleToolFactory()进行一次装配

然后使用全局变量 DoubleTool.函数()即可。

#pragma once
#include "common.h"
#include "a_string.h"
/**
* 标题:单例模式设计的double工具类
* 使用方法:调用DoubleToolFactory();进行装配,并调用全局变量DoubleTool结构体里的函数即可
*/
typedef struct doubleTool Double_Tool;
/// <summary>doubleTool:解决double问题的工具类</summary>
struct doubleTool {
	double MAX_VALUE;
	double MIN_VALUE;
	double (*Max)(double a, double b);
	double (*Min)(double a, double b);
	double (*sum)(double a, double b);

	//省略后续...
};
//-----Interface-----
Double_Tool DoubleTool;
//-----Factory-----
extern void DoubleToolFactory();

部分测试代码

	printf("Min:%lf\n", DoubleTool.Min(1.1, 10.1));
	printf("Sum:%lf\n", DoubleTool.sum(1.1, 10.1));
	printf("paresDouble:%lf\n", DoubleTool.parseDecDouble("3.1415926"));
	printf("toChar:%s\n", DoubleTool.toChar(DoubleTool.MAX_VALUE));
	printf("toString:%s\n", DoubleTool.toString(5)->data);
	double a[] = { 1.5,2.5,5,4.5,3.5 };
	DoubleTool.sortByDESC(a, ARR_SIZE(a));
	printf("SortDESC:%lf %lf %lf %lf %lf\n", a[0], a[1], a[2], a[3], a[4]);



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