头歌程序设计二(面向对象)_实训13_虚函数

第1关:设计和使用虚函数

测试说明

本关一共 4 个文件,其中List.h提供了 List 类。List 类提供 5 个虚函数,其中有 4 个是纯虚函数,需要用户在子类中实现。ArrayList.h提供了 ArrayList 类,LinkedList.h 提供了 LinkedList 类。这 2 个类都是 List 的派生类。
List.h 内容如下:

 
  1. #ifndef _LIST_H_
  2. #define _LIST_H_
  3. #include <iostream>
  4. using std::ostream;
  5. using std::endl;
  6. class List{
  7. protected:
  8. int size;
  9. public:
  10. //兼具默认构造函数和功能构造函数
  11. List(int s=0):size(s){}
  12. //拷贝构造函数
  13. List(const List&rhs):size(rhs.size){}
  14. /*以下为虚函数*/
  15. //作为继承的基类的析构函数一定要是虚的
  16. virtual ~List(){}
  17. /*以下为纯虚函数,即没有实现*/
  18. virtual void insert(int pos,int value)=0;
  19. virtual void remove(int pos)=0;
  20. virtual int at(int pos)const=0;
  21. virtual void modify(int pos,int newValue)=0;
  22. /*以下为普通的虚函数,有实现*/
  23. virtual void disp(ostream&os)const{
  24. for(int i=0,n=getSize();i<n;++i){
  25. os<<at(i)<<" ";//注意,这里at函数并没有实现
  26. //但不妨碍这么写
  27. }
  28. os<<endl;
  29. }
  30. };
  31. #endif // _LIST_H_

ArrayList.h 内容如下:

 
  1. #ifndef _ARRAYLIST_H_
  2. #define _ARRAYLIST_H_
  3. #include "List.h"
  4. class ArrayList : public List{
  5. private:
  6. int *data; //真正保存数据的地方
  7. int capacity;//容量
  8. public:
  9. //默认构造函数,构造一个逻辑为空的顺序表
  10. ArrayList();
  11. //拷贝构造函数,构造一个逻辑上与参数内容相同的顺序表
  12. ArrayList(const ArrayList&rhs);
  13. //原生数组构造函数,构造一个内容与给定数组相同的顺序表
  14. ArrayList(int const a[],int n);
  15. //填充构造函数,构造一个内容为n个value的顺序表
  16. ArrayList(int n,int value);
  17. //析构函数,一定要自行实现,否则有内存泄漏
  18. ~ArrayList();
  19. //子类当中须覆盖并实现父类中的纯虚函数
  20. void insert(int pos,int value);
  21. void remove(int pos);
  22. int at(int pos)const;
  23. void modify(int pos,int newValue);
  24. //对于父类中已实现的虚函数,可以选择覆盖或者不覆盖
  25. //void disp(ostream&os)const;//这个函数可以直接使用父类中的实现
  26. };
  27. #endif // _ARRAYLIST_H_

LinkedList.h 内容如下:

 
  1. #ifndef _LINKEDLIST_H_
  2. #define _LINKEDLIST_H_
  3. #include "List.h"
  4. class LinkedList : public List{
  5. public:
  6. //这是单链表节点的结构体
  7. struct Node{
  8. int data;
  9. Node *next;
  10. Node(int a=0,Node *b=nullptr):data(a),next(b){}
  11. };
  12. private:
  13. Node *head;//链表的头结点
  14. public:
  15. //默认构造函数,构造一个逻辑为空的链表
  16. LinkedList();
  17. //拷贝构造函数,构造一个逻辑上与参数内容相同的链表
  18. LinkedList(const LinkedList&rhs);
  19. //原生数组构造函数,构造一个内容与给定数组相同的链表
  20. LinkedList(int const a[],int n);
  21. //填充构造函数,构造一个内容为n个value的链表
  22. LinkedList(int n,int value);
  23. //析构函数,一定要自行实现,否则有内存泄漏
  24. ~LinkedList();
  25. //子类当中须覆盖并实现父类中的纯虚函数
  26. void insert(int pos,int value);
  27. void remove(int pos);
  28. int at(int pos)const;
  29. void modify(int pos,int newValue);
  30. //对于父类中已实现的虚函数,可以选择覆盖或者不覆盖
  31. //对于LinkedList子类,必须重新实现disp函数
  32. void disp(ostream&os)const;
  33. };
  34. #endif // _LINKEDLIST_H_

main.cpp 内容如下:

 
  1. #include <iostream>
  2. #include "List.h"
  3. #include "ArrayList.h"
  4. #include "LinkedList.h"
  5. using namespace std;
  6. int A[1000];
  7. int main(){
  8. //所有调用都通过基类的指针实现
  9. //如无特殊要求,绝不定义派生类类型的变量
  10. List *p = new ArrayList;
  11. int n;
  12. cin>>n;
  13. for(int i=0;i<n;++i){
  14. cin>>A[i];
  15. p->insert(p->getSize(),A[i]);
  16. }
  17. p->disp(cout);
  18. for(int i=0;i<3&&p->getSize()!=0;++i){
  19. p->remove(0);
  20. }
  21. p->disp(cout);
  22. for(int i=0;i<p->getSize();i+=2){
  23. p->modify(i,p->at(i)*10);
  24. }
  25. p->disp(cout);
  26. delete p;
  27. p = new LinkedList;
  28. for(int i=0;i<n;++i){
  29. p->insert(p->getSize(),A[i]);
  30. }
  31. p->disp(cout);
  32. for(int i=0;i<3;&&p->getSize()!=0;++i){
  33. p->remove(0);
  34. }
  35. p->disp();
  36. for(int i=0;i<p->getSize();i+=2){
  37. p->modify(i,p->at(i)*10);
  38. }
  39. p->disp(cout);
  40. delete p;
  41. return 0;
  42. }
#include "ArrayList.h"

/****************start from here**********************/
#include"List.h"
#include<iostream>
using namespace std;
  ArrayList::ArrayList()
    {
        data=new int[1005];
        capacity=0;
        size=0;
    }
   ArrayList::~ArrayList()
    {
        delete [] data;
    }

    void ArrayList::insert(int pos,int value)
    {
        data[pos]=value;
        capacity++;
        size++;
    }
    void ArrayList::remove(int pos)
    {
        for(int i=0;i<capacity;i++) 
            data[i]=data[i+1];
        capacity--;
        size--;
    }
    int ArrayList::at(int pos)const
    {
        return data[pos];
    }
    void ArrayList::modify(int pos,int newValue)
    {
        data[pos]=newValue;
    }

第2关:普通非成员函数的动态绑定

建立一个继承体系,List 是基类,ArrayListLinkedList 是其派生类。并且编写实现下述函数并达到如下效果。

 
  1. ostream& operator << (ostream&os, const List&rhs);

做一个流输出运算符重载,其第二个参数是List的常引用类型。我们知道子类的对象天生可以作为父类类型使用,因此

 
  1. ArrayList a;
  2. LinkedList b;
  3. operator << (cout,a);
  4. operator << (cout,b);

这上面的调用显然都是合法的。但是现在要求实现如下效果:第 3 行的函数执行的是适合 ArrayList 输出的代码,而第 4 行执行的是适合 LinkedList 输出的代码。即,虽然调用的函数一样,但需要根据当时的实参类型选择合适的实现。相当于对非成员函数做到动态绑定。

/****************start from here**********************/
#include "ArrayList.h"
#include"List.h"
#include<iostream>
using namespace std;
   ArrayList::ArrayList()
    {
        data=new int[1005];
        capacity=0;
        size=0;
    }
   ArrayList::~ArrayList()
    {
        delete [] data;
    }
    //
    void ArrayList::insert(int pos,int value)
    {
        data[pos]=value;
        capacity++;
        size++;
    }
    void ArrayList::remove(int pos)
    {
        for(int i=0;i<capacity;i++) 
            data[i]=data[i+1];
        capacity--;
        size--;
    }
    int ArrayList::at(int pos)const
    {
        return data[pos];
    }
    void ArrayList::modify(int pos,int newValue)
    {
        data[pos]=newValue;
    }

第3关:接口继承

测试说明

用户需要编写 IList 类,IList 类只提供 5 个纯虚函数,分别是增、删、查、改和显示函数。另外,考虑到 C++ 的语法需要,IList 类还需要定义一个虚函数的析构函数。
其他文件包括 List.h、ArrayList.h、ArrayList.cpp、LinkedList.h、LinkedList.cpp 和 main.cpp。
List.h 内容如下:

 
  1. #ifndef _LIST_H_
  2. #define _LIST_H_
  3. #include "IList.h"
  4. class List : public IList{//抽象父类直接继承接口
  5. protected:
  6. int size;
  7. public:
  8. //兼具默认构造函数和功能构造函数
  9. List(int s=0):size(s){}
  10. //拷贝构造函数
  11. List(const List&rhs):size(rhs.size){}
  12. //析构函数一定要是虚的
  13. virtual ~List(){}
  14. int getSize()const{return size;}
  15. /*现在不用再声明那些纯虚函数*/
  16. /*因为都从接口继承了*/
  17. /*这是一个可以实现的从接口继承而来的虚函数*/
  18. void disp(ostream&os)const{
  19. for(int i=0;i<size;++i){
  20. os<<at(i)<<" ";//注意,这里at函数并没有实现
  21. //但不妨碍这么写
  22. }
  23. os<<endl;
  24. }
  25. };
  26. #endif // _LIST_H_

ArrayList.h 内容如下:

 
  1. #ifndef _ARRAYLIST_H_
  2. #define _ARRAYLIST_H_
  3. #include "List.h"
  4. class ArrayList : public List{
  5. private:
  6. int *data; //真正保存数据的地方
  7. int capacity;//容量
  8. public:
  9. //默认构造函数,构造一个逻辑为空的顺序表
  10. ArrayList();
  11. //拷贝构造函数,构造一个逻辑上与参数内容相同的顺序表
  12. ArrayList(const ArrayList&rhs);
  13. //原生数组构造函数,构造一个内容与给定数组相同的顺序表
  14. ArrayList(int const a[],int n);
  15. //填充构造函数,构造一个内容为n个value的顺序表
  16. ArrayList(int n,int value);
  17. //析构函数,一定要自行实现,否则有内存泄漏
  18. ~ArrayList();
  19. /*虚函数,但是子类中可以不用写virtual*/
  20. void insert(int pos,int value);
  21. void remove(int pos);
  22. int at(int pos)const;
  23. void modify(int pos,int newValue);
  24. private:
  25. void setCapacity(int newCapa);
  26. };
  27. #endif // _ARRAYLIST_H_

ArrayList.cpp 的内容如下:

 
  1. #include "ArrayList.h"
  2. ArrayList::ArrayList():List(0),data(nullptr),capacity(0){
  3. capacity = 1;
  4. data = new int [capacity];
  5. }
  6. ArrayList::ArrayList(const ArrayList&rhs):List(rhs),data(nullptr),capacity(0){
  7. capacity = size;
  8. data = new int [capacity];
  9. for(int i=0;i<size;++i){
  10. data[i] = rhs.data[i];
  11. }
  12. }
  13. ArrayList::ArrayList(int const a[],int n):List(n),data(nullptr),capacity(0){
  14. capacity = size;
  15. data = new int [capacity];
  16. for(int i=0;i<size;++i){
  17. data[i] = a[i];
  18. }
  19. }
  20. ArrayList::ArrayList(int n,int value):List(n),data(nullptr),capacity(0){
  21. capacity = size;
  22. data = new int [capacity];
  23. for(int i=0;i<size;++i){
  24. data[i] = value;
  25. }
  26. }
  27. ArrayList::~ArrayList(){
  28. delete [] data;
  29. }
  30. void ArrayList::setCapacity(int newCapa){
  31. if ( newCapa <= capacity ){
  32. return;
  33. }
  34. int *p = new int [capacity=newCapa];
  35. for(int i=0;i<size;++i){
  36. p[i] = data[i];
  37. }
  38. delete data;
  39. data = p;
  40. }
  41. void ArrayList::insert(int pos,int value){
  42. if ( size == capacity ){
  43. setCapacity(capacity<<1);
  44. }
  45. for(int i=size-1;i>=pos;--i){
  46. data[i+1] = data[i];
  47. }
  48. data[pos] = value;
  49. ++size;
  50. }
  51. void ArrayList::remove(int pos){
  52. for(int i=pos;i<size-1;++i){
  53. data[i] = data[i+1];
  54. }
  55. --size;
  56. }
  57. int ArrayList::at(int pos)const{
  58. return data[pos];
  59. }
  60. void ArrayList::modify(int pos,int newValue){
  61. data[pos] = newValue;
  62. }

LinkedList.h 内容如下:

 
  1. #ifndef _LINKEDLIST_H_
  2. #define _LINKEDLIST_H_
  3. #include "List.h"
  4. class LinkedList : public List{
  5. public:
  6. //这是单链表节点的结构体
  7. struct Node{
  8. int data;
  9. Node *next;
  10. Node(int a=0,Node *b=nullptr):data(a),next(b){}
  11. };
  12. private:
  13. Node *head;//链表的头结点
  14. public:
  15. //默认构造函数,构造一个逻辑为空的链表
  16. LinkedList();
  17. //拷贝构造函数,构造一个逻辑上与参数内容相同的链表
  18. LinkedList(const LinkedList&rhs);
  19. //原生数组构造函数,构造一个内容与给定数组相同的链表
  20. LinkedList(int const a[],int n);
  21. //填充构造函数,构造一个内容为n个value的链表
  22. LinkedList(int n,int value);
  23. //析构函数,一定要自行实现,否则有内存泄漏
  24. ~LinkedList();
  25. /*虚函数,但是子类中可以不用写virtual*/
  26. void insert(int pos,int value);
  27. void remove(int pos);
  28. int at(int pos)const;
  29. void modify(int pos,int newValue);
  30. /*另外,出于效率考虑,还需要重新实现disp函数,覆盖基类的同名函数*/
  31. void disp(ostream&os)const;
  32. private:
  33. Node* advance(int pos)const;
  34. };
  35. #endif // _LINKEDLIST_H_

LinkedList.cpp 内容如下:

 
  1. #include "LinkedList.h"
  2. LinkedList::LinkedList():List(),head(nullptr){
  3. head = new Node;
  4. }
  5. LinkedList::LinkedList(const LinkedList&rhs):List(rhs),head(nullptr){
  6. head = new Node;
  7. Node *q = head;
  8. for(Node*p=rhs.head->next;p;p=p->next){
  9. q->next = new Node(p->data);
  10. q = q->next;
  11. }
  12. }
  13. LinkedList::LinkedList(int const a[],int n):List(n),head(nullptr){
  14. head = new Node;
  15. Node *q = head;
  16. for(int i=0;i<n;++i){
  17. q->next = new Node(a[i]);
  18. q = q->next;
  19. }
  20. }
  21. LinkedList::LinkedList(int n,int value):List(n),head(nullptr){
  22. head = new Node;
  23. Node *q = head;
  24. for(int i=0;i<n;++i){
  25. q->next = new Node(value);
  26. q = q->next;
  27. }
  28. }
  29. LinkedList::~LinkedList(){
  30. while( getSize() ){
  31. remove(0);
  32. }
  33. delete head;
  34. }
  35. LinkedList::Node* LinkedList::advance(int pos)const{
  36. Node *p=head;
  37. for(int i=-1;i<pos;++i)p=p->next;
  38. return p;
  39. }
  40. void LinkedList::insert(int pos,int value){
  41. Node *p = advance(pos-1);
  42. Node *q = new Node(value,p->next);
  43. p->next = q;
  44. ++size;
  45. }
  46. void LinkedList::remove(int pos){
  47. Node *p = advance(pos-1);
  48. Node *q = p->next;
  49. p->next = q->next;
  50. delete q;
  51. --size;
  52. }
  53. int LinkedList::at(int pos)const{
  54. return advance(pos)->data;
  55. }
  56. void LinkedList::modify(int pos,int newValue){
  57. advance(pos)->data = newValue;
  58. }
  59. void LinkedList::disp(ostream&os)const{
  60. for(Node*p=head->next;p;p=p->next){
  61. os<<p->data<<" ";
  62. }
  63. }

最后,main.cpp 内容如下:

 
  1. #include "List.h"
  2. #include "ArrayList.h"
  3. #include "LinkedList.h"
  4. using namespace std;
  5. int A[1000];
  6. int main(){
  7. List *p = new ArrayList;
  8. int n;
  9. cin>>n;
  10. for(int i=0;i<n;++i){
  11. cin>>A[i];
  12. p->insert(p->getSize(),A[i]);
  13. }
  14. p->disp(cout);
  15. for(int i=0;i<3&&p->getSize()!=0;++i){
  16. p->remove(0);
  17. }
  18. p->disp(cout);
  19. for(int i=0;i<p->getSize();i+=2){
  20. p->modify(i,p->at(i)*10);
  21. }
  22. p->disp(cout);
  23. delete p;
  24. p = new LinkedList;
  25. for(int i=0;i<n;++i){
  26. p->insert(p->getSize(),A[i]);
  27. }
  28. p->disp(cout);
  29. for(int i=0;i<3&&p->getSize()!=0;++i){
  30. p->remove(0);
  31. }
  32. p->disp(cout);
  33. for(int i=0;i<p->getSize();i+=2){
  34. p->modify(i,p->at(i)*10);
  35. }
  36. p->disp(cout);
  37. delete p;
  38. return 0;
  39. }
    #ifndef _ILIST_H_
    #define _ILIST_H_
    /****************start from here**********************/
    #include<iostream>
    using namespace std;
    class IList
    {
        public:
        virtual void insert(int pos,int value)=0;  
        virtual void remove(int pos)=0;  
        virtual int at(int pos)const=0;  
        virtual void modify(int pos,int newValue)=0;
        virtual void disp(ostream&os)const{};
    };
    
    
    #endif

C++ 中,虚函数是一种非常重要的概念,它允许子类重写父类的函数,并且在运行时调用正确的函数。 设计虚函数的一般步骤如下: 1. 在基类中定义虚函数使用键字 virtual。 2. 在派生类中重写虚函数,如果不需要重写,则直接继承即可。 3. 在使用时,使用基类指针或引用指向派生类对象,从而调用正确的函数。 下面是一个简单的例子,演示如何使用虚函数: ```c++ #include <iostream> class Shape { public: virtual void draw() { std::cout << "Drawing a shape..." << std::endl; } }; class Circle : public Shape { public: void draw() override { std::cout << "Drawing a circle..." << std::endl; } }; class Square : public Shape { public: void draw() override { std::cout << "Drawing a square..." << std::endl; } }; int main() { Shape* shape1 = new Circle(); Shape* shape2 = new Square(); shape1->draw(); // 输出 "Drawing a circle..." shape2->draw(); // 输出 "Drawing a square..." delete shape1; delete shape2; return 0; } ``` 在这个例子中,我们定义了一个基类 Shape 和两个派生类 Circle 和 Square。在基类 Shape 中定义了一个虚函数 draw,然后在派生类 Circle 和 Square 中重写了这个函数。在主函数中,我们使用基类指针指向派生类对象,然后调用了这个虚函数,从而实现了多态性。 值得注意的是,如果不使用虚函数,那么在使用基类指针或引用指向派生类对象时,将会调用基类中的函数,而不是派生类中的函数。这将导致程序无法实现多态性,而虚函数则解决了这个问题。
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值