1. 任務描述
建立一個繼承體系。List是基類,ArrayList和LinkedList是派生類。
List提供5個函式,分別是增刪查改顯。其中,前4個是純虛函式,第5個是虛函式。
用戶需在ArrayList和LinkedList中撰寫實作相應的實作。
注意一條:在ArrayList中無需再實作顯示函式。
2. 相關知識
虛函式是C++實作動態系結的關鍵。所謂動態系結,如下:
List *pList;
pList = new ArrayList;
pList->insert(pList->getSize()-1,someValue);//ArrayList做尾插入比較快
delete pList;
pList = new LinkedList;
pList->insert(0,someValue);//LinkedList做頭插入比較快
delete pList;
從語法上看,pList只是一個指向List型別的指標。但是,在第2、3行代碼中,pList實際上指向了一個ArrayList物件。此時通過pList呼叫了insert成員函式。那么,此時呼叫的是List類的insert函式還是ArrayList類的insert函式呢?這取決于是否為虛函式。
如果insert成員函式是虛函式,則此時呼叫的是ArrayList的成員函式。如果insert成員函式不是虛函式,則此時呼叫的是List的成員函式。
因此,如果將insert函式設定為虛函式,就可以實作這樣一種效果。如同上述代碼的第5、6行所寫。通過pList指標,既可以呼叫ArrayList的insert函式,也可以呼叫LinkedList的insert函式。
這樣做有什么好處呢?我們來描述這樣一種情形。需求是開發一款使用工業攝像頭的影像處理程式。于是購買了A公司的攝像頭產品,并使用其提供的圖形采集函式,編程的代碼如下:
A a;//A公司的攝像頭物件
a.initialize();//初始化
while(1){ //死回圈
a.grab();//采集一幀
/*這里作影像處理*/
}
過了一段時間,因為各種原因,要改用B公司的攝像頭,當然隨B公司也將隨產品提供其自身的函式。于是,我們需要修改代碼:
B a;//B公司的攝像頭物件
a.start();//初始化,為什么B公司的函式名不做成跟A公司一樣呢?
while(1){//死回圈
a.process();//采集一幀,這里不對B公司的取名抱任何期望
/*這里作影像處理*/
}
你會發現,如果代碼中涉及到的具體的攝像頭函式越多,當你改用別家產品的時候,你需要修改的代碼就越多。這意味著需要花費更多的人力、物力、時間,而且更容易出錯。
可以這樣做,仍然是先用A公司的,但是設計一個Camera基類,然后再派生一個類用于表示A公司的攝像頭:
class Camera{
public:
virtual void init();
virtual void getFrame();
};
class CA : public Camera{
private:
A a;//A公司的攝像頭物件
public:
void init(){a.initialize();}
void getFrame(){a.grab();}
}
Camera *pCamera = new CA;
pCamera->init();
while(1){
pCamera->getFrame();
/*這里作影像處理*/
}
現在需要換產品了,怎么辦?可以再添加一個CB類:
class CB : public Camera{
private:
B a;//B公司的攝像頭物件
public:
void init(){a.start();}
void getFrame(){a.process();}
};
對于我們的主程式,只需要改動一處即可:
//Camera *pCamera = new CA;
//只需改動這一句即可
Camera *pCamera = new CB;
pCamera->init();
while(1){
pCamera->getFrame();
/*這里作影像處理*/
}
后面這種編程的寫法,與前一種寫法相比,是更容易擴展、更容易升級的。當然要想獲得這個好處,必須同時做到2點:類的撰寫者必須提供虛函式,類的使用者必須使用基類的指標或者參考。
因此,當你作為類的撰寫者撰寫類的時候,特別是撰寫繼承體系時,必須提供合適的虛函式。當你作為類的使用者使用類的時候,必須優先定義基類的指標或者參考,如果沒有特殊理由,絕不使用子類的型別。
另外,注意一點:公有繼承的基類的解構式必須是虛的。
所謂純虛函式。C++允許一個類宣告一個純虛函式,即該虛函式未實作,沒有函式體。此時,該類是一個抽象類,它一定是作為基類使用的。純虛函式的作用,就是為其派生類規定函式的原型。這也被稱為介面。C++中并沒有專門描述介面的語法和關鍵字,C++中使用純虛函式來完成介面的功能(有關介面的內容可以參考第3關)。而Java中則將interface作為了一個關鍵字。
順便提一句,在一個繼承體系中,如果基類的某個函式被virtual修飾了,其派生類以及派生類的派生類等等都不需要再顯式的寫出virtual關鍵字,該函式在其所有派生類中均是虛的。
3. 編程要求
本關一共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;
}
uj5u.com熱心網友回復:
這是學習筆記嗎轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/234394.html
標籤:C++ 語言
上一篇:用vc6.0撰寫學生成績管理系統
下一篇:求解,不會做
