第1关:设计和使用虚函数
测试说明
本关一共 4 个文件,其中List.h提供了 List
类。List
类提供 5 个虚函数,其中有 4 个是纯虚函数,需要用户在子类中实现。ArrayList.h提供了 ArrayList
类,LinkedList.h 提供了 LinkedList
类。这 2 个类都是 List
的派生类。
List.h 内容如下:
#ifndef _LIST_H_
#define _LIST_H_
#include <iostream>
using std::ostream;
using std::endl;
class List{
protected:
int size;
public:
//兼具默认构造函数和功能构造函数
List(int s=0):size(s){}
//拷贝构造函数
List(const List&rhs):size(rhs.size){}
/*以下为虚函数*/
//作为继承的基类的析构函数一定要是虚的
virtual ~List(){}
/*以下为纯虚函数,即没有实现*/
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{
for(int i=0,n=getSize();i<n;++i){
os<<at(i)<<" ";//注意,这里at函数并没有实现
//但不妨碍这么写
}
os<<endl;
}
};
#endif // _LIST_H_
ArrayList.h 内容如下:
#ifndef _ARRAYLIST_H_
#define _ARRAYLIST_H_
#include "List.h"
class ArrayList : public List{
private:
int *data; //真正保存数据的地方
int capacity;//容量
public:
//默认构造函数,构造一个逻辑为空的顺序表
ArrayList();
//拷贝构造函数,构造一个逻辑上与参数内容相同的顺序表
ArrayList(const ArrayList&rhs);
//原生数组构造函数,构造一个内容与给定数组相同的顺序表
ArrayList(int const a[],int n);
//填充构造函数,构造一个内容为n个value的顺序表
ArrayList(int n,int value);
//析构函数,一定要自行实现,否则有内存泄漏
~ArrayList();
//子类当中须覆盖并实现父类中的纯虚函数
void insert(int pos,int value);
void remove(int pos);
int at(int pos)const;
void modify(int pos,int newValue);
//对于父类中已实现的虚函数,可以选择覆盖或者不覆盖
//void disp(ostream&os)const;//这个函数可以直接使用父类中的实现
};
#endif // _ARRAYLIST_H_
LinkedList.h 内容如下:
#ifndef _LINKEDLIST_H_
#define _LINKEDLIST_H_
#include "List.h"
class LinkedList : public List{
public:
//这是单链表节点的结构体
struct Node{
int data;
Node *next;
Node(int a=0,Node *b=nullptr):data(a),next(b){}
};
private:
Node *head;//链表的头结点
public:
//默认构造函数,构造一个逻辑为空的链表
LinkedList();
//拷贝构造函数,构造一个逻辑上与参数内容相同的链表
LinkedList(const LinkedList&rhs);
//原生数组构造函数,构造一个内容与给定数组相同的链表
LinkedList(int const a[],int n);
//填充构造函数,构造一个内容为n个value的链表
LinkedList(int n,int value);
//析构函数,一定要自行实现,否则有内存泄漏
~LinkedList();
//子类当中须覆盖并实现父类中的纯虚函数
void insert(int pos,int value);
void remove(int pos);
int at(int pos)const;
void modify(int pos,int newValue);
//对于父类中已实现的虚函数,可以选择覆盖或者不覆盖
//对于LinkedList子类,必须重新实现disp函数
void disp(ostream&os)const;
};
#endif // _LINKEDLIST_H_
main.cpp 内容如下:
#include <iostream>
#include "List.h"
#include "ArrayList.h"
#include "LinkedList.h"
using namespace std;
int A[1000];
int main(){
//所有调用都通过基类的指针实现
//如无特殊要求,绝不定义派生类类型的变量
List *p = new ArrayList;
int n;
cin>>n;
for(int i=0;i<n;++i){
cin>>A[i];
p->insert(p->getSize(),A[i]);
}
p->disp(cout);
for(int i=0;i<3&&p->getSize()!=0;++i){
p->remove(0);
}
p->disp(cout);
for(int i=0;i<p->getSize();i+=2){
p->modify(i,p->at(i)*10);
}
p->disp(cout);
delete p;
p = new LinkedList;
for(int i=0;i<n;++i){
p->insert(p->getSize(),A[i]);
}
p->disp(cout);
for(int i=0;i<3;&&p->getSize()!=0;++i){
p->remove(0);
}
p->disp();
for(int i=0;i<p->getSize();i+=2){
p->modify(i,p->at(i)*10);
}
p->disp(cout);
delete p;
return 0;
}
#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
是基类,ArrayList
和 LinkedList
是其派生类。并且编写实现下述函数并达到如下效果。
ostream& operator << (ostream&os, const List&rhs);
做一个流输出运算符重载,其第二个参数是List
的常引用类型。我们知道子类的对象天生可以作为父类类型使用,因此
ArrayList a;
LinkedList b;
operator << (cout,a);
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 内容如下:
#ifndef _LIST_H_
#define _LIST_H_
#include "IList.h"
class List : public IList{//抽象父类直接继承接口
protected:
int size;
public:
//兼具默认构造函数和功能构造函数
List(int s=0):size(s){}
//拷贝构造函数
List(const List&rhs):size(rhs.size){}
//析构函数一定要是虚的
virtual ~List(){}
int getSize()const{return size;}
/*现在不用再声明那些纯虚函数*/
/*因为都从接口继承了*/
/*这是一个可以实现的从接口继承而来的虚函数*/
void disp(ostream&os)const{
for(int i=0;i<size;++i){
os<<at(i)<<" ";//注意,这里at函数并没有实现
//但不妨碍这么写
}
os<<endl;
}
};
#endif // _LIST_H_
ArrayList.h 内容如下:
#ifndef _ARRAYLIST_H_
#define _ARRAYLIST_H_
#include "List.h"
class ArrayList : public List{
private:
int *data; //真正保存数据的地方
int capacity;//容量
public:
//默认构造函数,构造一个逻辑为空的顺序表
ArrayList();
//拷贝构造函数,构造一个逻辑上与参数内容相同的顺序表
ArrayList(const ArrayList&rhs);
//原生数组构造函数,构造一个内容与给定数组相同的顺序表
ArrayList(int const a[],int n);
//填充构造函数,构造一个内容为n个value的顺序表
ArrayList(int n,int value);
//析构函数,一定要自行实现,否则有内存泄漏
~ArrayList();
/*虚函数,但是子类中可以不用写virtual*/
void insert(int pos,int value);
void remove(int pos);
int at(int pos)const;
void modify(int pos,int newValue);
private:
void setCapacity(int newCapa);
};
#endif // _ARRAYLIST_H_
ArrayList.cpp 的内容如下:
#include "ArrayList.h"
ArrayList::ArrayList():List(0),data(nullptr),capacity(0){
capacity = 1;
data = new int [capacity];
}
ArrayList::ArrayList(const ArrayList&rhs):List(rhs),data(nullptr),capacity(0){
capacity = size;
data = new int [capacity];
for(int i=0;i<size;++i){
data[i] = rhs.data[i];
}
}
ArrayList::ArrayList(int const a[],int n):List(n),data(nullptr),capacity(0){
capacity = size;
data = new int [capacity];
for(int i=0;i<size;++i){
data[i] = a[i];
}
}
ArrayList::ArrayList(int n,int value):List(n),data(nullptr),capacity(0){
capacity = size;
data = new int [capacity];
for(int i=0;i<size;++i){
data[i] = value;
}
}
ArrayList::~ArrayList(){
delete [] data;
}
void ArrayList::setCapacity(int newCapa){
if ( newCapa <= capacity ){
return;
}
int *p = new int [capacity=newCapa];
for(int i=0;i<size;++i){
p[i] = data[i];
}
delete data;
data = p;
}
void ArrayList::insert(int pos,int value){
if ( size == capacity ){
setCapacity(capacity<<1);
}
for(int i=size-1;i>=pos;--i){
data[i+1] = data[i];
}
data[pos] = value;
++size;
}
void ArrayList::remove(int pos){
for(int i=pos;i<size-1;++i){
data[i] = data[i+1];
}
--size;
}
int ArrayList::at(int pos)const{
return data[pos];
}
void ArrayList::modify(int pos,int newValue){
data[pos] = newValue;
}
LinkedList.h 内容如下:
#ifndef _LINKEDLIST_H_
#define _LINKEDLIST_H_
#include "List.h"
class LinkedList : public List{
public:
//这是单链表节点的结构体
struct Node{
int data;
Node *next;
Node(int a=0,Node *b=nullptr):data(a),next(b){}
};
private:
Node *head;//链表的头结点
public:
//默认构造函数,构造一个逻辑为空的链表
LinkedList();
//拷贝构造函数,构造一个逻辑上与参数内容相同的链表
LinkedList(const LinkedList&rhs);
//原生数组构造函数,构造一个内容与给定数组相同的链表
LinkedList(int const a[],int n);
//填充构造函数,构造一个内容为n个value的链表
LinkedList(int n,int value);
//析构函数,一定要自行实现,否则有内存泄漏
~LinkedList();
/*虚函数,但是子类中可以不用写virtual*/
void insert(int pos,int value);
void remove(int pos);
int at(int pos)const;
void modify(int pos,int newValue);
/*另外,出于效率考虑,还需要重新实现disp函数,覆盖基类的同名函数*/
void disp(ostream&os)const;
private:
Node* advance(int pos)const;
};
#endif // _LINKEDLIST_H_
LinkedList.cpp 内容如下:
#include "LinkedList.h"
LinkedList::LinkedList():List(),head(nullptr){
head = new Node;
}
LinkedList::LinkedList(const LinkedList&rhs):List(rhs),head(nullptr){
head = new Node;
Node *q = head;
for(Node*p=rhs.head->next;p;p=p->next){
q->next = new Node(p->data);
q = q->next;
}
}
LinkedList::LinkedList(int const a[],int n):List(n),head(nullptr){
head = new Node;
Node *q = head;
for(int i=0;i<n;++i){
q->next = new Node(a[i]);
q = q->next;
}
}
LinkedList::LinkedList(int n,int value):List(n),head(nullptr){
head = new Node;
Node *q = head;
for(int i=0;i<n;++i){
q->next = new Node(value);
q = q->next;
}
}
LinkedList::~LinkedList(){
while( getSize() ){
remove(0);
}
delete head;
}
LinkedList::Node* LinkedList::advance(int pos)const{
Node *p=head;
for(int i=-1;i<pos;++i)p=p->next;
return p;
}
void LinkedList::insert(int pos,int value){
Node *p = advance(pos-1);
Node *q = new Node(value,p->next);
p->next = q;
++size;
}
void LinkedList::remove(int pos){
Node *p = advance(pos-1);
Node *q = p->next;
p->next = q->next;
delete q;
--size;
}
int LinkedList::at(int pos)const{
return advance(pos)->data;
}
void LinkedList::modify(int pos,int newValue){
advance(pos)->data = newValue;
}
void LinkedList::disp(ostream&os)const{
for(Node*p=head->next;p;p=p->next){
os<<p->data<<" ";
}
}
最后,main.cpp 内容如下:
#include "List.h"
#include "ArrayList.h"
#include "LinkedList.h"
using namespace std;
int A[1000];
int main(){
List *p = new ArrayList;
int n;
cin>>n;
for(int i=0;i<n;++i){
cin>>A[i];
p->insert(p->getSize(),A[i]);
}
p->disp(cout);
for(int i=0;i<3&&p->getSize()!=0;++i){
p->remove(0);
}
p->disp(cout);
for(int i=0;i<p->getSize();i+=2){
p->modify(i,p->at(i)*10);
}
p->disp(cout);
delete p;
p = new LinkedList;
for(int i=0;i<n;++i){
p->insert(p->getSize(),A[i]);
}
p->disp(cout);
for(int i=0;i<3&&p->getSize()!=0;++i){
p->remove(0);
}
p->disp(cout);
for(int i=0;i<p->getSize();i+=2){
p->modify(i,p->at(i)*10);
}
p->disp(cout);
delete p;
return 0;
}
#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