C++实验二:多态性应用

代码仅供参考
基本功能已实现,代码可自由复制、修改、转载。

课程名称: 面向对象技术(C++)
实验项目: 多态性应用
实验仪器: 计算机

  • 实验目的

1. 理解面向对象思想中泛型的概念、重要意义;

2. 正确声明和使用模板类;

  • 实验内容
  1. 仿照std::vector, 以模板类的形式实现一个通用的顺序存储容器 MyVector。
  2. 仿照std:: list, 以模板类的形式实现一个通用的链式存储容器MyList。
  3. 以模板类的形式实现栈和队列。

基本要求:

1)体现泛型思想、体现继承和派生;

2要给出关于设计思想的描述或是对设计过程做出必要说明和解释。

3)编写相应的测试用例来验证或者测试所设计类的基本功能及效率。

  • 三。
    MyVector
    这个类模板包含三个公有成员变量:Capacity、size 和 data。Capacity 表示当前数组的容量大小,size 表示数组中当前元素的数量,data 是指向数组元素的指针。
    template<class T>
    class MyVector{
    public:
        int Capacity;
        int size=0;
        T* data;
    1.默认构造
    MyVector()=default;
    2.析构函数,释放数据
        ~MyVector(){
            delete []data;
        }
    3.构造函数:接受一个int类型的数字n,n为MyVector容量大小
        MyVector(int n):Capacity{n},data{new T[n]}{}
    
    
    4.拷贝构造函数:接受一个 MyVector 类型的对象作为参数,创建一个新的 MyVector 类型的对象,它的数据与参数对象相同。
     MyVector(const MyVector &v):size{v.size},Capacity{v.Capacity},data{new T[v.Capacity]}{
            for(int i=0;i<size;i++){
                data[i]=v.data[i];
            }
        }
    
    
    
        Node *node=L.head->next;
       while(GetSize()<L.GetSize()) {
    
            insert_back(node->data);
    node=node->next;
    }
    
    }
    
    
      5.移动构造函数:接受一个右值引用的MyVector 类型对象作为参数,创建一个新的 MyVector 类型的对象,它的值与参数对象相同,并将参数对象的指针置为 null。
    
        MyVector(const MyVector &&v):size{v.size},Capacity{v.Capacity},data{v.data}{
            v.data=nullptr;
            v.size=0;
            v.Capacity=0;
    }
    
    6.添加数据模板函数 :接受一个T的对象t,把t送入此MyVector
        push(T t){
            data[size++]=t;
        }
    
        erase(){
            size--;
    
        }
    7.拷贝赋值运算符:接受一个 MyVector类型的对象作为参数,将当前对象的值设置为参数对象的值。
    
        MyVector & operator=(const MyVector &v){
            delete []data;
            size=v.size;
            Capacity=v.Capacity;
            data=new T [Capacity];
             for(int i=0;i<size;i++){
                data[i]=v.data[i];
            }
    
    
    
    
            }
    
    8.索引运算符:重载 [] 运算符,接受一个整数类型的索引作为参数,返回当前对象中对应位置的数据。
            T& operator[](int index){
                if(index<size)return data[index];
                else {cout<<"索引越界"<<endl;
                exit(1);}
            }
    
    9.输出运算符:重载 << 运算符,用于将 MyVector类型的对象的全部数据输出到标准输出流。
        friend std::ostream &operator<<(std::ostream& out,const MyVector &v){
            for(int i=0;i<v.size;i++){
                out<<v.data[i]<<endl;
            }
            return out;
    
        }
    10.输入运算符:重载>>运算符,用于将 MyVector类型的对象的数据写入。
    
        friend std::istream &operator>>(istream &input,MyVector &v){
            int n;
            T t;
           cout<<"输入要输入的个数:"<<endl;
           cin>>n;
            for(int i=0;i<n;i++){
                    cout<<"输入第"<<i<<"个数据:"<<endl;
                    cin>>t;
                v.push(t);
            }
            return input;
        }
    
    
    };
    
    
    
    
    MyList:
    这个类模板包含两个个公有成员变量:Capacity、size 和 。Capacity 表示当前数组的容量大小,size 表示数组中当前元素的数量。还有一个结构体Node,用来实现List数据存储
    
    
    template<class T>
    class MyList{
    public:
        int size=0;
        int Capacity=0;
    
    
        typedef struct Node{
            T data;
            Node *next;
            Node *prev;
            Node(T d):data(d){}
            Node(){}
        }Node;
    
        Node *head,*tail,*temp;
    1.默认构造
    
        MyList()=default;
    
    2.析构函数:使用循环从头节点开始把每个节点内存释放
       ~MyList(){
        Node *dt=head->next;
           while(dt!=tail){
                Node *cd=dt;
                dt=dt->next;
    
                delete cd;
    
    
           }
           delete head;
           delete tail;
    
        }
    
    3.构造函数:接受一个int类型的数字n,n为MyList容量大小
        MyList(int n):Capacity{n},head{new Node},tail{new Node}{
            temp=head;
            head->next=tail;
            tail->prev=head;
    
    
        }
    
    
        int GetSize()const{
            return this->size;
        }
    4.拷贝构造函数:接受一个 MyListr 类型的对象作为参数,创建一个新的 MyList 类型的对象,它的数据与参数对象相同。使用循环把数据拷贝到新的MyList对象上
    
    //拷贝构造
    
    MyList(const MyList &L):head{new Node},tail(new Node),Capacity{L.Capacity}{
        temp=head;
        head->next=tail;
        tail->prev=head;
    
    
    
    
        Node *node=L.head->next;
       while(GetSize()<L.GetSize()) {
    
            insert_back(node->data);
    node=node->next;
    }
    
    }
    
    
    5.移动构造函数:接受一个右值引用的MyList 类型对象作为参数,创建一个新的 MyList类型的对象,它的值与参数对象相同,并将参数对象的指针head与tail设置为null。
    
    
    //移动构造
        MyList( MyList &&L):size{L.size},Capacity{L.Capacity}{
            head=L.head;
            tail=L.tail;
            temp=L.temp;
            L.head=L.tail=L.temp=nullptr;
    
        }
    
    6.添加数据模板函数 :接受一个T的对象t,使用尾插法把t送入此MyList
    
        void insert_back(T t){
    
        Node *node=new Node(t);
        tail->prev=node;
        node->next=tail;
        node->prev=temp;
        temp->next=node;
        temp=temp->next;
        size++;
    }
    
    
    
    7.添加数据模板函数 :接受一个T的对象t,使用头插法把t送入此MyList
    
    void insert_front(T t){
    
    
        Node *node=new Node(t);
        node->prev=head;
        node->next=head->next;
        head->next->prev=node;
        head->next=node;
        size++;
    }
    8.删除数据函数 :从尾部删除一个元素
    
        void pop_back(){
          size--;
            Node *tt=temp;
            temp->prev->next=tail;
            tail->prev=temp->prev;
            temp=tail->prev;
        delete tt;
    
    
        }
    9.删除数据函数 :从头部删除一个元素
    
        void pop_front(){
           size--;
            Node *tt=head->next;
            head->next->next->prev=head;
            head->next=head->next->next;
    
            delete tt;
        }
    
    9.查找数据函数 :查找MyList中第n个元素
    
    T GetByOrder(int n){
        if(n>size){
            cout<<"索引越界"<<endl;
            exit(1);
        }
    
        Node *ts=head;
        for(int i=0;i<=n;i++){
        ts=ts->next;
        }
        return ts->data;
    
    
    }
    
    10.拷贝赋值运算符:接受一个 MyList类型的对象作为参数,将当前对象的值设置为参数对象的值。
    
    MyList& operator=(const MyList &L)    {
        this->size=0;
    
    
        Node *node=L.head->next;
       while(GetSize()<L.GetSize()) {
            insert_back(node->data);
       node=node->next;
       return *this;
    
    }}
    
    
    11.输出运算符:重载 << 运算符,用于将 MyList类型的对象的全部数据输出到标准输出流。
    
    friend std::ostream &operator <<(ostream &out,const MyList &L){
        for(Node *i=L.head->next;i!=L.tail;i=i->next){
            cout<<i->data<<endl;
        }
        return out;
    }
    
    
    12.输入运算符:重载>>运算符,用于将 MyList类型的对象的数据写入。
    
    friend std::istream & operator>>(istream &in,MyList &L){
        int n;
        cout<<"输入要输入数据的个数:";
        cin>>n;
    
        T t;
        for(int i=0;i<n;i++){
                cout<<"请输入第"<<i<<"个数据:";
            cin>>t;
           L.insert_back(t);
        }
        return in;
    }
    
    
    
    
    MyStack:
    这个类模板继承MyVector:其中有Capacity、size,*data继承自MyVector 。
    
    template<class T>
    class MyStack:public MyVector<T>{
    public:
    1.默认构造
        MyStack()=default;
    
    
    2.构造函数:接受一个int类型的数字n,n为MyStack容量大小
    
        MyStack(int n):MyVector<T>(n){}
    3.析构函数:因为Stack没有成员变量,什么都不需要执行,这个会自动执行基类的析构函数
    
        ~MyStack(){
        }
    
    //拷贝构造
    4.拷贝构造函数:接受一个 Mystack 类型的对象作为参数,创建一个新的 MyStack类型的对象,它的数据与参数对象相同。使用循环把数据拷贝到新的MyStack对象上
    
         MyStack(const MyStack &s):MyVector<T>{s.size}{
               for(int i=0;i<s.size;i++){
                this->data[i]=s.data[i];;
                this->size++;
            }
    }
    
    
    
    5.移动构造函数:接受一个右值引用的MyStack 类型对象作为参数,创建一个新的 MyStack类型的对象,它的值与参数对象相同,因为没有成员变量,直接调用父类的移动构造
    
    //移动构造
        MyStack(MyStack &&s):MyVector<T>{std::move(s)}{
    
        }
    
    
    
    
    
    6.删除数据函数 :从尾部删除一个元素
    
        void pop(){
            *this->size--;
        }
    7.查询数据函数 :按顺序查询第n个数据
    
        T GetElement(int n){
            return this->data[n];
        }
    8.输出运算符:重载 << 运算符,用于将 MyStack类型的对象的全部数据输出到标准输出流。
    
        friend std::ostream &operator<<(std::ostream& out,const MyStack &v){
            cout<<"数据为:"<<endl;
            for(int i=0;i<v.size;i++){
                out<<v.data[i]<<endl;
            }
            return out;
    
        }
    9.输入运算符:重载>>运算符,用于将 MyStack类型的对象的数据写入。
    
        friend std::istream &operator>>(istream &input,MyStack &v){
            int n;
            T t;
           cout<<"输入要输入的个数:"<<endl;
           cin>>n;
            for(int i=0;i<n;i++){
                    cout<<"输入第"<<i<<"个数据:"<<endl;
                    cin>>t;
                v.push(t);
            }
            return input;
        }
    
    
    
    };
    
    
    Myqueue:
    
    这个类模板继承MyList:其中有Capacity、size,Node继承自MyList 
    template<class T>
    class Myqueue:public MyList<T>{
        public:
    1.重新定义基类的Node,方便后续使用
    typedef typename MyList<T>::Node Node;
    1.默认构造
    Myqueue()=default;
    
    2.构造函数:接受一个int类型的数字n,n为Myqueue容量大小
        Myqueue(int n):MyList<T>(n){}
    
    
    //拷贝构造
    3.拷贝构造函数:接受一个 Myqueue 类型的对象作为参数,创建一个新的 Myqueue类型的对象,它的数据与参数对象相同。使用循环把数据拷贝到新的Myqueue对象上
        Myqueue(const Myqueue &q){
    
        this->head=new Node;
        this->tail=new Node;
            this->head->next=this->tail;
            this->tail->prev=this->head;
            this->temp=this->head;
            Node * tp=q.head->next;
    
            while(tp!=q.tail){
                    this->insert_back(tp->data);
            tp=tp->next;
    
            }
    
            }
    5.移动构造函数:接受一个右值引用的Myqueue类型对象作为参数,创建一个新的 Myqueue类型的对象,它的值与参数对象相同,因为没有成员变量,直接调用父类的移动构造
    
            //移动构造
            Myqueue(Myqueue &&q):MyList<T>{std::move(q)}{
          
            }
    
    
    
    6.添加数据模板函数 :接受一个T的对象t,使用头插法把t送入此Myqueue
        void enque(T t){
            this->insert_back(t);
        }
    
    
    7.删除数据函数 :调用基类函数,从头部删除一个元素
    
        void deque(){
           this->pop_front();
        }
    
    8..查询数据函数 :按顺序查询第n个数据
    
        T GetElement(int n){
            Node *no=this->head->next;
            for(int i=0;i<n;i++){
                no=no->next;
            }
            return no->data;
        }
    
    
    9.输入运算符:重载>>运算符,用于将 Myqueue类型的对象的数据写入
        friend std::istream &operator>>(istream &input,Myqueue &v){
            int n;
            T t;
           cout<<"输入要输入的个数:"<<endl;
           cin>>n;
            for(int i=0;i<n;i++){
                    cout<<"输入第"<<i<<"个数据:"<<endl;
                    cin>>t;
                v.enque(t);
            }
            return input;
        }
    
    
    
    
    10.输出运算符:重载 << 运算符,用于将 Myqueue类型的对象的全部数据输出到标准输出流。
    
        friend std::ostream& operator<<(ostream &out,Myqueue &q){
            cout<<"数据为:"<<endl;
            Node *t=q.head->next;
            for(int i=0;i<q.size;i++,t=t->next){
                cout<<t->data<<endl;
            }
            return out;
        }
    };

测试代码

  • 实验总结

泛型是面向对象编程中非常重要的概念之一,它提供了一种通用的、与具体类型无关的数据类型定义方式,使得代码的复用性和可扩展性更强。在使用泛型时,不需要事先知道具体数据类型,而是在运行时再确定具体的类型,在处理不同类型的数据时能够更加灵活和方便。

C++ 中的泛型可以使用模板类(template class)实现。模板类可以用来定义一种通用的类模板,可以在该模板的基础上创建出具体的类型。使用模板类需要指定模板参数,这些参数用来代替具体的数据类型,在实例化时确定具体的类型。这样可以避免为每种数据类型编写单独的代码。

通过实现以上数据结构,深入掌握了面向对象思想中泛型概念和模板类的使用方法,提高了我的编程技能和代码复用性。同时,通过对具体数据结构的实现,深入理解了这些数据结构的内部原理和实现方式。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值