实验环境
IDE: Microsoft Visual Studio Code October 2022 (version 1.71.3)
编译器:MingW-W64-builds 10.0.0 & GCC 12.1.0
操作系统:Windows 11 专业版22H2 22623.891 Beta Preview(64-bit)
题目一
Ex . 1 【本题考察知识点:结构体,动态内存的分配,动态内存的扩充、缩小】- 更新整数集合库的实现:设计一个包含int类型元素的数学集合库,完成cpp文件,实现该整数集合库。数学集合是包含不同对象的集合,例如{1、3、9}和{5、11、11}= {5, 11}. 根据下面的集合头文件,在相应的cpp文件中写入必要的用于处理集合的函数(创建、删除集合以及添加和删除元素)。该整数集合库的头文件和测试程序如下。
uset.h
/**
* @file uset.h
* @brief 集合库头文件
*/
#ifndef USET_H
#define USET_H
#define INITSETSIZE 64 // 为集合分配的初始内存
/**
* @brief 包含int类型元素的数学集合
*
*/
typedef struct universalSet{
int* elem; //元素列表
int card; //集合的基数
}uset;
/**
* @brief 初始化一个空集合并为其分配初始内存
*
* @param set
*/
void newSet(uset** set);
/**
* @brief 释放newSet函数分配的内存
*
* @param set
*/
void deleteSet(uset** set);
/**
* @brief 添加元素到集合中;检查元素是否已经在集合中;
* 当集合的基数=分配的内存时调整内存大小,新内存的大小等于原内存大小+64
* e.g. before: mem=128, card=128, after: men=192, card=129
*
* @param elem 元素
* @param set
*/
void addElem(int* elem,uset* set);
/**
* @brief 从集合中移除元素;如果集合不包含此元素,则不执行任何操作;
* 如果认为使用了太多内存,则调整内存大小,新内存的大小等于原内存大小-64
* e.g. before: mem=192, card=129, after: card=128, mem=128
*
* @param elem
* @param set
*/
void remElem(int* elem,uset* set);
#endif
main.cpp
#include <iostream>
#include "uset.h"
using namespace std;
//打印集合中的元素数量以及全部元素
void displaySet(uset* pset);
int main(){
uset *myset;
newSet(&myset);
// 注: 这里将&myset 传入函数newSet, 目的是可以修改myset的值
// 这里&myset 的类型是 uset **
displaySet(myset);
for(int i=1;i<80;i++)
addElem(&i, myset);
displaySet(myset);
for(int i=50;i<100;i++)
addElem(&i,myset);
displaySet(myset);
for(int i=30;i<100;i++)
remElem(&i,myset);
displaySet(myset);
deleteSet(&myset);
return 0;
}
- 【提示】一旦发现card已经是64的倍数,则加一个元素一定会超出原来的内存空间能力,此时,就应该申请一个新的空间,这个新空间的大小(元素个数)应该是previous+64(即:先前的元素个数+64)
- 【提示】一旦发现card-1是64的倍数,则删除一个元素后,集合中元素的个数与原来申请的内存空间的大小之差,刚好是64,此时有64个元素大小的内存空间未被使用。因此,此时,就可以申请一个较小的新的空间,这个新空间的大小(元素个数)应该是previous-64(即:先前的元素个数-64)
问题分析与算法设计
1. main.cpp 主程序 uset.h 集合库
2. uset_MenageMemory.cpp 集合内存分配与回收
- 主要实现了newSet和deleteSet函数
3. uset_OperateElem.cpp 对集合中的元素进行操作
- 主要实现了addElem和remElem函数
- 添加了一个函数头resizeMemory,通过if语句判断用来动态调整内存大小
- 添加算法概述:指针遍历确认无重复项→在(set->elem+set->card)的位置添加新元素→集合基数自加→对集合中的元素进行升序排列
- 移除算法概述:指针遍历查找目标项→目标项值归零→对集合降序排列,此时目标项对应值0被移到最后→集合基数自减,0被排除出元素列表→重新升序排列集合
- 内存管理算法概述:if检查是否满足调整条件→对已分配的内存AM值进行±64的操作→以新AM值为长度重新分配内存给一个临时集合tempSet→把set->elem的内容copy到tempSet中→delete以回收set->elem的内存空间→将tempSet指向set->elem,完成操作
4.uset_Display.cpp 打印集合中的元素个数和所有元素
- 主要实现了displaySet函数 -所有元素以{元素,……}的形式输出,空集合则输出空集符号
源代码
uset_Display.cpp
#include <iostream>
#include "uset.h"
using namespace std;
void displaySet(uset* pset){
cout<<"集合中的元素个数为: "<<pset->card<<endl;
if(pset->card==0){
cout<<"集合中所有元素为: ∅\n";
}else{
cout<<"集合中所有元素为: {";
for(int i=0;i<pset->card;i++){
cout<<*((int*)pset->elem+i);
if(i!=(pset->card)-1)
cout<<",";
}
cout<<"}\n";
}
cout<<"************************************\n";
}
uset_MenageMemory.cpp
#include<iostream>
#include"uset.h"
using namespace std;
void newSet(uset** set){
*set=new uset;
(*set)->card=0; //集合的初始基数为0
(*set)->elem=new int[INITSETSIZE]; //分配给集合的初始内存大小为64
}
void deleteSet(uset** set){
delete [] *set; //释放内存
}
uset_OperateElem.cpp
#include<iostream>
#include"uset.h"
#include<algorithm>
using namespace std;
//重新动态调整内存大小
void resizeMemory(int* allocated_memory,uset* set);
void addElem(int* elem,uset* set){
bool exist=0; //表示elem是否已经在set中存在 //元素列表
int allocated_memory=_msize(set->elem)/sizeof(elem); //当前分配给集合的内存
for(int i=0;i<set->card;i++){
if(*(set->elem+i)==*elem){
exist=1;
break;
}
} //查重,如果不存在则继续以下操作
if(!exist){
*(set->elem+set->card)=*elem; //赋值
set->card++; //基数自加
sort(set->elem,set->elem+set->card); //升序排列数组
resizeMemory(&allocated_memory,set);
}
}
void remElem(int* elem,uset* set){
int allocated_memory=_msize(set->elem)/sizeof(elem); //当前分配给集合的内存
for(int i=0;i<set->card;i++){
if(*(set->elem+i)==*elem){ //查询到存在则操作,否则结束操作
*(set->elem+i)=0; //将元素归零
sort(set->elem,set->elem+set->card);
reverse(set->elem,set->elem+set->card); //两步完成逆向排列,0被移到集合最后
set->card--; //基数自减
sort(set->elem,set->elem+set->card);
resizeMemory(&allocated_memory,set);
}
}
}
void resizeMemory(int* allocated_memory,uset* set){
int AM=*allocated_memory;
if(set->card==AM){
AM+=64; //扩展分配的内存
int* tempSet=new int [AM];
copy((set->elem),(set->elem)+AM-64,tempSet);
delete [] set->elem;
*allocated_memory=AM;
set->elem=tempSet;
}else if(set->card==AM-64){
AM-=64; //缩减分配的内存
int* tempSet=new int [AM];
copy((set->elem),(set->elem)+AM,tempSet);
delete [] set->elem;
*allocated_memory=AM;
set->elem=tempSet;
}
}
实验小结
1.调用函数时需要关注参数的类型
2.注意tempSet指向set->elem之后已经成为了野指针,不能再对其进行delete操作
3.已分配内存的计算公式:allocated_memory=_msize(set->elem)/sizeof(elem);
题目二
Ex. 2【本题考察知识点:无类型指针void *,对不同类型分情况讨论】
将Ex.1中的整数集合一般化,设计一个通用的数学集合库,实现该数学集合库。假设该集合可以包含char、int或double类型的元素。例如{1,3,9},{‘r‘,’g‘,’b‘},and{5,11,11}={5,11},{0.12,0.8,0.12}={0.8,0.12}。根据下面的通用集合头文件,在相应cpp文件中写入必要的用于处理集合的函数(创建、删除集合以及添加和删除元素)。假设一个集合可以包含char、int或double类型的元素。该数学集合库的头文件和测试程序如下。
uset.h
/**
* @file uset.h
* @brief 集合库头文件
*/
#ifndef USET_H
#define USET_H
#define INITSETSIZE 64 // 为集合分配的初始内存
/**
* @brief 包含char、int或double类型元素的数学集合
*
*/
typedef struct universalSet{
void* elem; //元素列表
int card; //集合的基数
int type; //集合存储的数据类型(1:char;2:int,3:double)
}uset;
/************************************************
* 由uset_MenageMemory.cpp实现
* 主要用于对集合进行分配内存和释放内存的操作
************************************************/
/**
* @brief 初始化一个空集合并为其分配初始内存
*
* @param set 集合
* @param type 集合存储的数据类型(1:char;2:int,3:double)
*/
void newSet(uset** set,int type);
/**
* @brief 释放newSet函数分配的内存
*
* @param set 集合
*/
void deleteSet(uset** set);
/************************************************
* 由uset_OperateElem.cpp实现
* 主要用于对集合进行添加和删减元素的操作
* 同时兼具了根据集合的基数管理动态内存的功能
************************************************/
/**
* @brief 添加元素到集合中;检查元素是否已经在集合中;
* 当集合的基数=分配的内存时调整内存大小,新内存的大小等于原内存大小+64
* e.g. before: mem=128, card=128, after: men=192, card=129
*
* @param elem 元素
* @param set 集合
*/
void addElem(void* elem,uset* set);
/**
* @brief 从集合中移除元素;如果集合不包含此元素,则不执行任何操作;
* 如果认为使用了太多内存,则调整内存大小,新内存的大小等于原内存大小-64
* e.g. before: mem=192, card=129, after: card=128, mem=128
*
* @param elem 元素
* @param set 集合
*/
void remElem(void* elem,uset* set);
/************************************************
* 由uset_Display.cpp实现
* 主要用于显示集合中元素的个数和所有的元素
************************************************/
/**
* @brief 显示集合pset中元素的个数和所有的元素
*
* @param pset
*/
void displaySet(uset* pset);
/************************************************
* 由uset_ReadElem.cpp实现
* 主要用于实现读取元素的操作
************************************************/
/**
* @brief 检查集合pset是否包含元素elem
*
* @param elem
* @param pset
* @return true
* @return false
*/
bool contains(void* elem,uset* pset);
/**
* @brief 查找元素elem在集合pset中的位置并且返回,如果不存在,则返回-1
*
* @param elem
* @param pset
* @return int
*/
int findPos(void* elem,uset* pset);
#endif
main.cpp
#include<iostream>
#include"uset.h"
using namespace std;
int main(){
uset * myset1;
newSet(&myset1,1);
displaySet(myset1);
int i;
char ch;
for(i=1;i<15;i++){
ch='a'+i;
addElem(&ch,myset1);
}
displaySet(myset1);
for(i=10; i<25;i++){
ch='a'+i;
addElem(&ch,myset1);
}
displaySet(myset1);
for(i=10;i<20;i++){
ch='a'+i;
remElem(&ch,myset1);
}
displaySet(myset1);
deleteSet(&myset1);
uset *myset2;
newSet(&myset2,2);
displaySet(myset2);
for(i=1;i<80;i++)
addElem(&i,myset2);
displaySet(myset2);
for(i=50;i<100;i++)
addElem(&i,myset2);
displaySet(myset2);
for(i=30;i<100;i++)
remElem(&i,myset2);
displaySet(myset2);
deleteSet(&myset2);
uset *myset3;
newSet(&myset3,3);
displaySet(myset3);
double dx;
int j;
for(j=1;j<80;j++){
dx=j*10.15;
addElem(&dx,myset3);
}
displaySet(myset3);
deleteSet(&myset3);
return 0;
}
问题分析与算法设计
1.main.cpp 主程序 uset.h 集合库
2. uset_MenageMemory.cpp 集合内存分配与回收
- 主要实现了newSet和deleteSet函数
- 通过switch(type)来判断set->elem的具体类型并且分配对应空间
3.uset_ReadElem.cpp 对集合中的元素进行读取
- 主要实现了contains和findPos函数
- contains算法概述:for循环遍历查找→switch(pset->type)决定类型→case中由if判断存在,存在则直接返回true→for循环完整结束,无结果,返回false
- findPos算法概述:和contains类似,在返回true处返回i的值,在返回false处返回-1
4. uset_OperateElem.h 对集合中的元素进行操作的额外函数库
- 包含了三个函数模板,对Ex1中的添加,移除和内存管理算法进行了封装
- 添加 template<class DA>void doAdd(DA elem,uset* set);
- 移除 template<class DR>void doRemove(DR elem,uset* set);
- 内存管理 template<class RM>void resizeMemory(RM elem,int* AM,uset* set);
5. uset_OperateElem.cpp 对集合中的元素进行操作
- 主要实现了addElem和remElem函数,也实现了额外库中的三个函数模板
- 经过优化后的添加/移除算法概述:通过contains函数判断元素是否存在于集合中→switch(set->type)决定类型→计算当前分配内存→doAdd/doRemove→resizeMemory
4.uset_Display.cpp 打印集合中的元素个数和所有元素
- 主要实现了displaySet函数
- 所有元素以{元素,……}的形式输出,空集合则输出空集符号∅
源代码
uset_Display.cpp
#include <iostream>
#include "uset.h"
using namespace std;
void displaySet(uset* pset){
cout<<"集合中的元素个数为: "<<pset->card<<endl;
if(pset->card==0){
cout<<"集合中所有元素为: ∅\n";
}else{
cout<<"集合中所有元素为: {";
for(int i=0;i<pset->card;i++){
switch(pset->type){
case 1: cout<<*((char*)pset->elem+i); break;
case 2: cout<<*((int*)pset->elem+i); break;
case 3: cout<<*((double*)pset->elem+i); break;
}
if(i!=(pset->card)-1)
cout<<",";
}
cout<<"}\n";
}
cout<<"************************************\n";
}
uset_MenageMemory.cpp
#include<iostream>
#include"uset.h"
using namespace std;
void newSet(uset** set,int type){
(*set)=new uset;
(*set)->card=0; //集合的初始基数为0
switch(type){
case 1:{
(*set)->type=1; //指定类型为字符集合
(*set)->elem=new char[INITSETSIZE]; //分配给字符集合的初始内存大小为64
break;
}
case 2:{
(*set)->type=2; //指定类型为整形集合
(*set)->elem=new int[INITSETSIZE]; //分配给整形集合的初始内存大小为64
break;
}
case 3:{
(*set)->type=3; //指定类型为浮点集合
(*set)->elem=new double[INITSETSIZE]; //分配给浮点集合的初始内存大小为64
break;
}
}
}
void deleteSet(uset** set){
delete [] *set; //释放内存
}
uset_OperateElem.h
/**
* @file uset_OperateElem.h
* @brief 集合元素操作头文件
*/
#ifndef USET_OPERATEELEM_H
#define USET_OPERATEELEM_H
#include"uset.h"
/**
* @brief 对集合中的元素进行添加操作的函数模板
*
* @tparam DA - 可用的代替包括char,int,double
* @param elem 加入的元素
* @param set 集合
*/
template<class DA>void doAdd(DA elem,uset* set);
/**
* @brief 对集合中的元素进行添加操作的函数模板
*
* @tparam DR - 可用的代替包括char,int,double
* @param elem 移除的元素
* @param set 集合
*/
template<class DR>void doRemove(DR elem,uset* set);
/**
* @brief 在对集合中的元素进行操作后动态管理内存的函数模板
*
* @tparam RM
* @param elem 变动的元素,以便模板获知类型
* @param allocated_memory 已经分配的内存
* @param set 集合
*/
template<class RM>void resizeMemory(RM elem,int* allocated_memory,uset* set);
#endif
uset_OperateElem.cpp
#include<iostream>
#include<algorithm>
#include"uset.h"
#include"uset_OperateElem.h"
using namespace std;
void addElem(void* elem,uset* set){
if(!contains(elem,set)){
switch(set->type){
case 1:{
char char_elem=*(char*)elem; //强制转换类型后的临时元素
int allocated_memory=_msize((char*)set->elem)/sizeof(char_elem); //当前分配给集合的内存
doAdd(char_elem,set);
resizeMemory(char_elem,&allocated_memory,set);
break;
}
case 2:{
int int_elem=*(int*)elem; //强制转换类型后的临时元素
int allocated_memory=_msize((int*)set->elem)/sizeof(int_elem); //当前分配给集合的内存
doAdd(int_elem,set);
resizeMemory(int_elem,&allocated_memory,set);
break;
}
case 3:{
double double_elem=*(double*)elem; //强制转换类型后的临时元素
int allocated_memory=_msize((double*)set->elem)/sizeof(double_elem);//当前分配给集合的内存
doAdd(double_elem,set);
resizeMemory(double_elem,&allocated_memory,set);
break;
}
}
}
}
void remElem(void* elem,uset* set){
if(contains(elem,set)){
switch(set->type){
case 1:{
char char_elem=*(char*)elem; //强制转换类型后的临时元素
int allocated_memory=_msize((char*)set->elem)/sizeof(char_elem); //当前分配给集合的内存
doRemove(char_elem,set);
resizeMemory(char_elem,&allocated_memory,set);
break;
}
case 2:{
int int_elem=*(int*)elem; //强制转换类型后的临时元素
int allocated_memory=_msize((int*)set->elem)/sizeof(int_elem); //当前分配给集合的内存
doRemove(int_elem,set);
resizeMemory(int_elem,&allocated_memory,set);
break;
}
case 3:{
double double_elem=*(double*)elem; //强制转换类型后的临时元素
int allocated_memory=_msize((double*)set->elem)/sizeof(double_elem);//当前分配给集合的内存
doRemove(double_elem,set);
resizeMemory(double_elem,&allocated_memory,set);
break;
}
}
}
}
template<class DA>void doAdd(DA elem,uset* set){
*((DA*)set->elem+set->card)=elem; //赋值
set->card++; //基数自加
sort(((DA*)set->elem),((DA*)set->elem)+set->card); //升序排列数组
}
template<class DR>void doRemove(DR elem,uset* set){
*((DR*)set->elem+findPos(&elem,set))=0; //元素归零
sort(((DR*)set->elem),((DR*)set->elem)+set->card);
reverse(((DR*)set->elem),((DR*)set->elem)+set->card); //两步完成逆向排列,0被移到集合最后
set->card--; //基数自减
sort(((DR*)set->elem),((DR*)set->elem)+set->card); //重新排序
}
template<class RM>void resizeMemory(RM elem,int* allocated_memory,uset* set){
int AM=*allocated_memory;
if(set->card==AM){
AM+=64; //扩展分配的内存
void* tempSet=tempSet=new RM [AM];
copy(((RM*)set->elem),((RM*)set->elem)+AM-64,(RM*)tempSet);
delete [] (RM*)set->elem;
*allocated_memory=AM;
set->elem=tempSet;
}else if(set->card==AM-64){
AM-=64; //缩减分配的内存
void* tempSet=tempSet=new RM [AM];
copy(((RM*)set->elem),((RM*)set->elem)+AM,(RM*)tempSet);
delete [] (RM*)set->elem;
*allocated_memory=AM;
set->elem=tempSet;
}
}
uset_ReadElem.cpp
#include<iostream>
#include"uset.h"
using namespace std;
bool contains(void* elem,uset* pset){
for(int i=0;i<pset->card;i++){
switch(pset->type){
case 1:{
if(*((char*)pset->elem+i)==*(char*)elem)
return true;
break;
}
case 2:{
if(*((int*)pset->elem+i)==*(int*)elem)
return true;
break;
}
case 3:{
if(*((double*)pset->elem+i)==*(double*)elem)
return true;
break;
}
}
}
return false;
}
int findPos(void* elem,uset* pset){
for(int i=0;i<pset->card;i++){
switch(pset->type){
case 1:{
if(*((char*)pset->elem+i)==*(char*)elem)
return i;
break;
}
case 2:{
if(*((int*)pset->elem+i)==*(int*)elem)
return i;
break;
}
case 3:{
if(*((double*)pset->elem+i)==*(double*)elem)
return i;
break;
}
}
}
return -1;
}
实验小结
1.函数模板可以有效降低重复代码量
2.void*指针
- void*是无类型的指针,仅存储内存地址,不指定目标类型。正常来说,如果两个指针类型不一样的话,两个指针变量是不可以直接相等的。例如int*a,float*b,假如令a=b,会直接发生编译错误,而void*指针可以指向任何类型的指针。但是反过来不可以,也就是说,一个有类型的指针不能指向一个void*类型的变量(哪怕此时void*变量已经指向了一个有类型的地址)
- 因为void*指针的目标类型不明确,不能直接解引用。void*指针只有强制类型转换以后才可以正常取值。
- 当void*指针作为函数的输入和输出时,表示可以接受任意类型的输入指针和输出任意类型的指针。
3.swtich和case语句中,定义变量要加花括号
题目三
Ex 3.【本题考察知识点:类的定义、实现、使用】 设计一个整数集合类。与Ex2.相同,该类提供用于处理集合的函数(创建、删除集合以及添加和删除元素)。
问题分析与算法设计
1.main.cpp 主程序
2.uset.h 集合库
- 大部分函数的算法与Ex2类似
- 私有成员包括elem,card,type
- 私有函数包括findPos,函数模板doAdd,doRemove和resizeRemory
- 公有函数包括构造和析构函数,contains,addElem,remElem和displaySet
3. uset_MenageMemory.cpp 集合内存分配与回收
- 主要实现了构造函数和析构函数
4.uset_ReadElem.cpp 对集合中的元素进行读取
- 主要实现了contains和findPos函数
5. uset_OperateElem.cpp 对集合中的元素进行操作
- 主要实现了addElem和remElem函数,也实现了uset类私有的三个函数模板
4.uset_Display.cpp 打印集合中的元素个数和所有元素
- 主要实现了displaySet函数
源代码
main.cpp
#include<iostream>
#include"uset.h"
using namespace std;
int main(){
uset myset(1);
myset .displaySet();
int i;
char ch;
for(i=1;i<15;i++){
ch='a'+i;
myset.addElem(&ch);
}
myset.displaySet();
for(i=10;i<25;i++){
ch='a'+i;
myset.addElem(&ch);
}
myset.displaySet();
for(i=10;i<20;i++){
ch='a'+i;
myset.remElem(&ch);
}
myset.displaySet();
uset myset2(2);
myset2.displaySet();
for(i=1;i<80;i++)
myset2.addElem(&i);
myset2.displaySet();
for(i=50;i<100;i++)
myset2.addElem(&i);
myset2.displaySet();
for(i=30;i<100;i++)
myset2.remElem(&i);
myset2.displaySet();
uset myset3(3);
myset3.displaySet();
double dx;
int j;
for(j=1; j<80;j++){
dx=j*10.15;
myset3.addElem(&dx);
}
myset3.displaySet();
return 0;
}
uset.h
/**
* @file uset.h
* @brief 集合库头文件
*/
#ifndef USET_H
#define USET_H
#define INITSETSIZE 64 // 为集合分配的初始内存
class uset{
private:
void* elem; //元素列表
int card; //集合的基数
int type; //集合存储的数据类型(1:char;2:int,3:double)
/**
* @brief 查找元素elem在集合pset中的位置并且返回,如果不存在,则返回-1
*
* @param elem
* @return int
*/
int findPos(void* elem);
/**
* @brief 对集合中的元素进行添加操作的函数模板
*
* @tparam DA - 可用的代替包括char,int,double
* @param elem 加入的元素
* @param set 集合
*/
template<class DA>void doAdd(DA elem);
/**
* @brief 对集合中的元素进行添加操作的函数模板
*
* @tparam DR - 可用的代替包括char,int,double
* @param elem 移除的元素
* @param set 集合
*/
template<class DR>void doRemove(DR elem);
/**
* @brief 在对集合中的元素进行操作后动态管理内存的函数模板
*
* @tparam RM
* @param elem 变动的元素,以便模板获知类型
* @param allocated_memory 已经分配的内存
* @param set 集合
*/
template<class RM>void resizeMemory(RM elem,int* allocated_memory);
public:
/************************************************
* 由uset_MenageMemory.cpp实现
* 主要用于对集合进行分配内存和释放内存的操作
************************************************/
/**
* @brief 初始化一个空集合并为其分配初始内存
*
* @param type 集合存储的数据类型(1:char;2:int,3:double)
*/
uset(int type);
/**
* @brief 释放newSet函数分配的内存
*
*/
~uset();
/************************************************
* 由uset_OperateElem.cpp实现
* 主要用于对集合进行添加和删减元素的操作
* 同时兼具了根据集合的基数管理动态内存的功能
************************************************/
/**
* @brief 添加元素到集合中;检查元素是否已经在集合中;
* 当集合的基数=分配的内存时调整内存大小,新内存的大小等于原内存大小+64
* e.g. before: mem=128, card=128, after: men=192, card=129
*
* @param elem 元素
*/
void addElem(void* elem);
/**
* @brief 从集合中移除元素;如果集合不包含此元素,则不执行任何操作;
* 如果认为使用了太多内存,则调整内存大小,新内存的大小等于原内存大小-64
* e.g. before: mem=192, card=129, after: card=128, mem=128
*
* @param elem 元素
*/
void remElem(void* elem);
/************************************************
* 由uset_Display.cpp实现
* 主要用于显示集合中元素的个数和所有的元素
************************************************/
/**
* @brief 显示集合pset中元素的个数和所有的元素
*
*/
void displaySet();
/************************************************
* 由uset_ReadElem.cpp实现
* 主要用于实现读取元素的操作
************************************************/
/**
* @brief 检查集合pset是否包含元素elem
*
* @param elem 元素
* @return true
* @return false
*/
bool contains(void* elem);
};
#endif
uset_Display.cpp
#include <iostream>
#include "uset.h"
using namespace std;
void uset::displaySet(){
cout<<"集合中的元素个数为: "<<card<<endl;
if(card==0){
cout<<"集合中所有元素为: ∅\n";
}else{
cout<<"集合中所有元素为: {";
for(int i=0;i<card;i++){
switch(type){
case 1: cout<<*((char*)elem+i); break;
case 2: cout<<*((int*)elem+i); break;
case 3: cout<<*((double*)elem+i); break;
}
if(i!=(card)-1)
cout<<",";
}
cout<<"}\n";
}
cout<<"************************************\n";
}
uset_MenageMemory.cpp
#include<iostream>
#include"uset.h"
using namespace std;
uset::uset(int type){
card=0; //集合的初始基数为0
switch(type){
case 1:{
uset::type=1; //指定类型为字符集合
elem=new char[INITSETSIZE]; //分配给字符集合的初始内存大小为64
break;
}
case 2:{
uset::type=2; //指定类型为整形集合
elem=new int[INITSETSIZE]; //分配给整形集合的初始内存大小为64
break;
}
case 3:{
uset::type=3; //指定类型为浮点集合
elem=new double[INITSETSIZE]; //分配给浮点集合的初始内存大小为64
break;
}
}
}
uset::~uset(){
if(elem){
switch(type){
case 1: delete [] (char*)elem; break;
case 2: delete [] (int*)elem; break;
case 3: delete [] (double*)elem; break;
}
}
}
uset_OperateElem.cpp
#include<iostream>
#include<algorithm>
#include"uset.h"
using namespace std;
void uset::addElem(void* elem){
if(!uset::contains(elem)){
switch(type){
case 1:{
char char_elem=*(char*)elem; //强制转换类型后的临时元素
int allocated_memory=_msize((char*)uset::elem)/sizeof(char_elem); //当前分配给集合的内存
doAdd(char_elem);
resizeMemory(char_elem,&allocated_memory);
break;
}
case 2:{
int int_elem=*(int*)elem; //强制转换类型后的临时元素
int allocated_memory=_msize((int*)uset::elem)/sizeof(int_elem); //当前分配给集合的内存
doAdd(int_elem);
resizeMemory(int_elem,&allocated_memory);
break;
}
case 3:{
double double_elem=*(double*)elem; //强制转换类型后的临时元素
int allocated_memory=_msize((double*)uset::elem)/sizeof(double_elem);//当前分配给集合的内存
doAdd(double_elem);
resizeMemory(double_elem,&allocated_memory);
break;
}
}
}
}
void uset::remElem(void* elem){
if(uset::contains(elem)){
switch(type){
case 1:{
char char_elem=*(char*)elem; //强制转换类型后的临时元素
int allocated_memory=_msize((char*)uset::elem)/sizeof(char_elem); //当前分配给集合的内存
doRemove(char_elem);
resizeMemory(char_elem,&allocated_memory);
break;
}
case 2:{
int int_elem=*(int*)elem; //强制转换类型后的临时元素
int allocated_memory=_msize((int*)uset::elem)/sizeof(int_elem); //当前分配给集合的内存
doRemove(int_elem);
resizeMemory(int_elem,&allocated_memory);
break;
}
case 3:{
double double_elem=*(double*)elem; //强制转换类型后的临时元素
int allocated_memory=_msize((double*)uset::elem)/sizeof(double_elem);//当前分配给集合的内存
doRemove(double_elem);
resizeMemory(double_elem,&allocated_memory);
break;
}
}
}
}
template<class DA>void uset::doAdd(DA elem){
*((DA*)uset::elem+card)=elem; //赋值
card++; //基数自加
sort(((DA*)uset::elem),((DA*)uset::elem)+card); //升序排列数组
}
template<class DR>void uset::doRemove(DR elem){
*((DR*)uset::elem+findPos(&elem))=0; //元素归零
sort(((DR*)uset::elem),((DR*)uset::elem)+card);
reverse(((DR*)uset::elem),((DR*)uset::elem)+card); //两步完成逆向排列,0被移到集合最后
card--; //基数自减
sort(((DR*)uset::elem),((DR*)uset::elem)+card); //重新排序
}
template<class RM>void uset::resizeMemory(RM elem,int* allocated_memory){
int AM=*allocated_memory;
if(card==AM){
AM+=64; //扩展分配的内存
void* tempSet=new RM [AM];
copy(((RM*)uset::elem),((RM*)uset::elem)+AM-64,(RM*)tempSet);
delete [] (RM*)uset::elem;
*allocated_memory=AM;
uset::elem=tempSet;
}else if(card==AM-64){
AM-=64; //缩减分配的内存
void* tempSet=new RM [AM];
copy(((RM*)uset::elem),((RM*)uset::elem)+AM,(RM*)tempSet);
delete [] (RM*)uset::elem;
*allocated_memory=AM;
uset::elem=tempSet;
}
}
uset_ReadElem.cpp
#include<iostream>
#include"uset.h"
using namespace std;
bool uset::contains(void* elem){
for(int i=0;i<card;i++){
switch(type){
case 1:{
if(*((char*)uset::elem+i)==*(char*)elem)
return true;
break;
}
case 2:{
if(*((int*)uset::elem+i)==*(int*)elem)
return true;
break;
}
case 3:{
if(*((double*)uset::elem+i)==*(double*)elem)
return true;
break;
}
}
}
return false;
}
int uset::findPos(void* elem){
for(int i=0;i<card;i++){
switch(type){
case 1:{
if(*((char*)uset::elem+i)==*(char*)elem)
return i;
break;
}
case 2:{
if(*((int*)uset::elem+i)==*(int*)elem)
return i;
break;
}
case 3:{
if(*((double*)uset::elem+i)==*(double*)elem)
return i;
break;
}
}
}
return -1;
}
实验小结
1.外部不需要调用的函数尽量放在私有成员中,只保留与外部必要的接口即可
2.当命名产生冲突时(比如函数参数中有elem,类私有成员中也有elem),在类成员中加上类名前缀(如uset::elem),否则虽然不会产生报错,但是在实际运行中会出现数据异常
题目四
Ex4.【本题考察知识点:类的定义、实现、使用】定义和实现一个复数类,能够进行复数的加法、乘法、输入和输出,并测试你所定义的复数类:(1)加法规则:(a+bi)+(c+di)=(a+c)+(b+d)i;(2)乘法规则:(a+bi)x(c+di)=(ac-bd)+(bc+ad)i;(3)输入规则:以a+bi的形式输入,如2+3i;(4)输出规则:以a+bi的形式输出,如2+3i。
问题分析与算法设计
1.main.cpp 主程序
2. complex.h复数库
- 定义了复数类Complex
- 私有成员包括real,imag
- 公有函数包括构造函数,output,multi
3. complex.cpp
- 主要实现了类中的公有函数
- 算法实现基本参照题设
源代码
main.cpp
#include <iostream>
#include "complex.h"
using namespace std;
int main(){
double real, imag;
cout << "请输入第一个复数的实部、虚部:" << endl;
cin >> real >> imag;
Complex a(real,imag);
cout << "请输入第二个复数的实部、虚部:" << endl;
cin >> real >> imag;
Complex b(real,imag);
Complex c;
c.add(a, b);
cout << "两个复数的和为:";
c.output(); cout<<"\n";
c.multi(a, b);
cout << "两个复数的积为:";
c.output(); cout<<"\n";
return 0;
}
complex.h
#ifndef COMPLEX_H
#define COMPLEX_H
class Complex{
private:
double real; // 复数的实部
double imag; // 复数的虚部
public:
/**
* @brief 构造一个复数类
*
* @param realx 实部
* @param imagy 虚部
*/
Complex(double reZ=0, double imZ=0);
/**
* @brief 打印一个复数
*
*/
void output();
/**
* @brief 复数加法
*
* @param a
* @param b
*/
void add(Complex &a,Complex &b);
/**
* @brief 复数乘法
*
* @param a
* @param b
*/
void multi(Complex &a,Complex &b);
};
#endif
complex.cpp
#include<iostream>
#include"complex.h"
using namespace std;
Complex::Complex(double reZ, double imZ){
real=reZ;
imag=imZ;
}
void Complex::output(){
cout<<real<<"+"<<imag<<"i";
}
void Complex::add(Complex &a,Complex &b){
Complex::real=a.real+b.real;
Complex::imag=a.imag+b.imag;
}
void Complex::multi(Complex &a,Complex &b){
Complex::real=a.real*b.real-a.imag*b.imag;
Complex::imag=a.imag*b.real+a.real*b.imag;
}
题目五
Ex.5【本题考察知识点:类的定义、实现、使用】
设计一个类Clock,模拟一个用于显示时间的电子时钟,该时钟以时、分、秒的形式记录时间。请编写3个函数:setTime用于设置时钟的时间;increase函数模拟时间过去了1秒;showTime显示当前时间,格式为HH:MM:SS。
问题分析与算法设计
1.main.cpp 主程序
2. complex.h复数库
- 定义了时钟类Clock
- 私有成员包括hour,min,sec
- 私有函数包括add0
- 公有函数包括setTime,increase,showTime
3. clock.cpp
- 主要实现了类中的公有函数
- 算法实现基本参照题设
- add0函数:把数字转换为string类,如果不满两位在前部补上0
源代码
main.cpp
#include<iostream>
#include"clock.h"
using namespace std;
int main(){
Clock c;
cout << "请输入当前时间:" << endl;
c.setTime();
int n;
cout << "请输入模拟的秒数:" << endl;
cin >> n;
for(int i = 0; i < n; i++){
c.increase();
c.showTime();
}
return 0;
}
clock.h
#ifndef CLOCK_H
#define CLOCK_H
#include<iostream>
#include<string>
using namespace std;
class Clock{
private:
int hour; //时
int min; //分
int sec; //秒
//int转string类,如果非两位则在前部补0
string add0(int n);
public:
//以HH:MM:SS格式读入当前时间
void setTime();
//increase函数模拟时间过去了1秒
void increase();
//showTime显示当前时间,格式为HH:MM:SS。
void showTime();
};
#endif
clock.cpp
#include<iostream>
#include<string>
#include<vector>
#include"clock.h"
using namespace std;
void Clock::setTime(){
string input,record;
vector<string> time; //time[0]=HH,time[1]=MM,time[2]=SS
cin>>input;
for(int i=0;i<input.size();i++){
if(input[i]!=':'){
record.push_back(input[i]);
}else{
time.push_back(record);
record.clear();
}
}
time.push_back(record);
record.clear();
hour=stoi(time[0]);
min=stoi(time[1]);
sec=stoi(time[2]);
}
void Clock::increase(){
if(hour==23&&min==59&&sec==59){
hour=0; min=0; sec=0; //日结归零
}else if(hour!=23&&min==59&&sec==59){
hour++; min=0; sec=0; //时进位
}else if(hour!=23&&min!=59&&sec==59){
min++; sec=0; //分进位
}else{
sec++; //秒进位
}
}
string Clock::add0(int n){
return n<10 ? "0"+to_string(n) : to_string(n);
}
void Clock::showTime(){
string time;
time=add0(hour)+":"+add0(min)+":"+add0(sec);
cout<<time<<endl;
}