程序设计基础作业06-结构体、模块化开发、类

实验环境

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;
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇