在C++中有很豐富的庫,當屬STL模板,STL的設計和優化都為我們提供了應有的功能。然而對於新手而言,嘗試進行一個封裝,會使得自己更加熟悉面向對象。
面向對象三大特性:封裝、繼承、多態。這也是面向對對象語言相對面向過程而言,最大的優勢和特點。面向對象使得程序更加利於維護,讓設計人員更加關注設計,要想真正的理解面向對象的特性,則必須要清楚和掌握這三大規律。
在C++中,STL提供了Vector類,表示向量,其本質則是線性表的實現,並且可以在內部實現自動擴容,並且借助迭代器,可以很方便很快速的對表中元素進行訪問和遍歷。
因此我們手動封裝的線性表,可以有以下的功能:
自動擴容 迭代器訪問元素 下標式快速訪問元素值 移除元素 增加元素 長度管理上述這些功能,都是需要一個向量所需要提供的功能,並且基於面向對象的設計而言,采用迭代器模式的設計,可以很好的減小容器對象與數據之間的緊密耦合,通過迭代器去遍歷向量表,可以很大程度的增加遍歷的安全性和便利性。
在該設計中,可進行三個類的設計: Sequeence 抽象基類,ZArray類,Iterator 抽象基類, ZArrayIterator類,其中兩個抽象基類中分別提供了序列類的基本操作,增刪查改等,Iterator基本定義了迭代器的方法接口,可以很好的實現多態,並且可以擁有很好的擴展性,因為對序列數據容器而言,其邏輯結構可以是連續的向量式的,也可以是鏈式的,由此他們之間的遍歷方式都不同,訪問方式也不同,因此為了可擴展性,可以進行抽象,使得迭代器和序列容器進行交互,而無需使用具體的子類,並且可以很好的進行擴展。那麼在這裡,面向對向的特性則充分包含了進去,封裝是完好類設計的基礎,繼承則是擴展的必經之路,多態則是在面向抽象編程的基礎。
值得注意的是,由於在C++中容器類都采用模板的方式去進行封裝,由此需要注意的是,最好將.h的定義和.cpp的定義為一個文件中,否則在某些編譯環境中可能造成link錯誤!
//
// Sequence.hpp
// Array
//
// Created by 鄒智鵬 on 16/7/3.
// Copyright ? 2016年 Frank. All rights reserved.
//
#ifndef Sequence_hpp
#define Sequence_hpp
#include
#include "Iterator.hpp"
namespace ZTemplate {
typedef unsigned int z_size; // 用於表示大小,為無符號整形
typedef long rank; // 用於表示秩
template
class Sequence {
public:
/**
* 定義函數指針,用於表示兩個值的比較, 若兩個值相等,返回0,若val1 > val2 返回1, 否則返回-1
*/
typedef int (*__FUNC_COMPARE_)(const T &val1, const T &val2);
/**
* 插入到最後一個位置
*/
virtual void push_back(const T& val) = 0;
/**
* 從指定位置中,移除元素,並返回該元素的副本,若為指針,請自行進行內存管理
* @param pos 元素位置
* @return 返回該元素值
*/
virtual T pop(const rank pos) = 0;
/**
* 訪問指定位置的值
* @param pos 元素所在的位置
* @return 返回該位置的元素值
*/
virtual const T &at(const rank pos) const = 0;
/**
* 移除指定位置元素
* @param pos 元素所在位置
* @return 返回是否移除成功
*/
virtual bool remove(const rank pos) = 0;
/**
* 根據指定值,在序列中進行查找,若查找到符合條件的則進行移除
*/
virtual bool remove(T &val1, __FUNC_COMPARE_ compare);
/**
* 獲取迭代器
* @return 返回迭代器
*/
virtual Iterator &iterator() = 0;
/**
* 重載訪問器
* @param pos 元素位置
* @return 返回元素引用
*/
virtual T& operator[](const rank pos) = 0;
protected:
};
}
template
bool ZTemplate::Sequence::remove(T &val1, __FUNC_COMPARE_ compare) {
Iterator &curIterator = iterator(); // 獲取到迭代器實例
bool found = false;
rank i = 0; // 秩
while (!found && curIterator.hasNext()) {
if (compare(val1, curIterator.data()) == 0) {
// 兩者值相等
found = true;
break;
}
i++;
curIterator = curIterator.next();
}
return remove(i);// 移除指定位置
}
#endif /* Sequence_hpp */
對迭代器進行抽象
迭代器需要提供的功能為容器的訪問:下一個、是否右下一個、獲取當前迭代器的值元素
//
// Iterator.hpp
// Array
//
// Created by 鄒智鵬 on 16/7/3.
// Copyright ? 2016年 Frank. All rights reserved.
//
#ifndef Iterator_hpp
#define Iterator_hpp
#include
namespace ZTemplate {
template
class Iterator {
public:
/**
* 默認構造函數
*/
Iterator(){}
/**
* 下一個元素
* @return 返回下一個迭代器
*/
virtual Iterator &next() = 0;
/**
* 獲取迭代器值
* @return 返回值
*/
virtual const T data() const = 0;
/**
* 是否含有下一個元素
* @return 返回是否有後續元素
*/
virtual bool hasNext() = 0;
/**
* 重載++
*/
virtual Iterator& operator++() = 0;
virtual Iterator& operator++(int) = 0;
};
}
#endif /* Iterator_hpp */
在上面的過程中,我們已經定義了所需要的接口,並且對各種容器和所需要的迭代器進行了抽象,擁有了統一的接口,我們可以針對不同的實例進行擴展。在這裡,我們先對向量進行擴展!
對向量的封裝實現
//
// Array.hpp
// Array
//
// Created by 鄒智鵬 on 16/7/3.
// Copyright ? 2016年 Frank. All rights reserved.
//
#ifndef Array_hpp
#define Array_hpp
#include
#include
#include "Sequence.hpp"
namespace ZTemplate {
template
class ZArrayIterator;
template
class ZArray : public Sequence{
public:
/**
* 默認構造
*/
ZArray();
/**
* 根據容量構造向量
* @param capacity 容量
*/
ZArray(z_size capacity);
/**
* 插入到最後一個位置
*/
virtual void push_back(const T &val);
/**
* 從指定位置中,移除元素,並返回該元素的副本,若為指針,請自行進行內存管理
* @param pos 元素位置
* @return 返回該元素值
*/
virtual T pop(const rank pos);
/**
* 訪問指定位置的值
* @param pos 元素所在的位置
* @return 返回該位置的元素值
*/
virtual const T &at(const rank pos) const;
/**
* 移除指定位置元素
* @param pos 元素所在位置
* @return 返回是否移除成功
*/
virtual bool remove(const rank pos);
/**
* 獲取迭代器
* @return 返回迭代器
*/
virtual Iterator &iterator();
/**
* 返回長度信息
* @return 返回數組的有效長度
*/
z_size length()const{return _length;}
/**
* 重載訪問器
* @param pos 元素位置
* @return 返回元素引用
*/
T& operator[](const rank pos);
/**
* 析構函數
*/
~ZArray();
friend class ZArrayIterator;
protected:
T *_array; // 實際存儲空間
z_size _length; // 數組長度
z_size capacity; // 最大容量
Iterator *_iterator; // 迭代器
/**
* 縮容
*/
T *shink();
/**
* 擴容
*/
T *expand();
};
/**迭代器類*/
template
class ZArrayIterator : public Iterator {
protected:
rank pointTo;// 當前游標
ZArray *_array;
public:
/**
* 構造函數
* @param array 用於遍歷數組
*/
ZArrayIterator(ZArray &arr):Iterator(){this->_array = &arr;}
/**
* 下一個元素
* @return 返回下一個迭代器
*/
virtual Iterator &next();
/**
* 獲取迭代器值
* @return 返回值
*/
virtual const T1 data() const;
/**
* 是否含有下一個元素
* @return 返回是否有後續元素
*/
virtual bool hasNext();
/**
* 重置前置++
*/
virtual Iterator& operator++();
/**
* 重載後置++
*/
virtual Iterator& operator++(int);
};
}
template
ZTemplate::ZArray::ZArray():Sequence() {
static z_size size = 5;
_array = new T[size];
capacity = size;
_length = 0;
_iterator = new ZArrayIterator(*this);
}
template
ZTemplate::ZArray::ZArray(z_size capacity) {
_array = new T[capacity];
this->capacity = capacity;
_length = 0;
_iterator = new ZArrayIterator(*this);
}
template
void ZTemplate::ZArray::push_back(const T &val) {
if (_length == capacity) {
_array = expand();
}
_array[_length] = val;
_length++;
}
template
T ZTemplate::ZArray::pop(const rank pos) {
T val = _array[pos]; // 要返回的值
remove(pos);
return val;
}
template
const T& ZTemplate::ZArray::at(const rank pos) const{
return _array[pos];
}
template
bool ZTemplate::ZArray::remove(const rank pos) {
if (pos >= _length) {
return false;
}
for (rank i = pos; i < _length - 1; i++) {
_array[i] = _array[i + 1];
}
_length--;
return true;
}
template
T* ZTemplate::ZArray::shink() {
T *newLocate = new T[capacity >> 1];
for (int i = 0; i < _length; i++) {
newLocate[i] = _array[i];
}
delete _array;
_array = newLocate;
return newLocate;
}
template
T * ZTemplate::ZArray::expand() {
T *newLocate = new T[capacity << 1];
for (int i = 0; i < _length; i++) {
newLocate[i] = _array[i];
}
delete _array;
_array = newLocate;
return _array;
}
template
ZTemplate::ZArray::~ZArray() {
delete _array;
_array = nullptr;
_length = 0;
capacity = 0;
}
template
T & ZTemplate::ZArray::operator[](const rank pos) {
return _array[pos];
}
template
ZTemplate::Iterator& ZTemplate::ZArrayIterator::next() {
pointTo++;
return *this;
}
template
bool ZTemplate::ZArrayIterator::hasNext() {
return pointTo < _array->length();
}
template
const T1 ZTemplate::ZArrayIterator::data() const {
return _array->_array[pointTo];
}
template
ZTemplate::Iterator &ZTemplate::ZArray::iterator() {
return *(new ZArrayIterator(*this));
}
template
ZTemplate::Iterator &ZTemplate::ZArrayIterator::operator++() {
next();
return *this;
}
template
ZTemplate::Iterator &ZTemplate::ZArrayIterator::operator++(int i) {
ZTemplate::Iterator &it = *(new ZArrayIterator(*this));
next();
return it;
}
#endif /* Array_hpp */
對向量迭代器的具體實現
/**迭代器類*/
template
class ZArrayIterator : public Iterator {
protected:
rank pointTo;// 當前游標
ZArray *_array;
public:
/**
* 構造函數
* @param array 用於遍歷數組
*/
ZArrayIterator(ZArray &arr):Iterator(){this->_array = &arr;}
/**
* 下一個元素
* @return 返回下一個迭代器
*/
virtual Iterator &next();
/**
* 獲取迭代器值
* @return 返回值
*/
virtual const T1 data() const;
/**
* 是否含有下一個元素
* @return 返回是否有後續元素
*/
virtual bool hasNext();
/**
* 重置前置++
*/
virtual Iterator& operator++();
/**
* 重載後置++
*/
virtual Iterator& operator++(int);
};
上述的迭代器的具體實現,由於避免循環包含的緣故,需要定義在同一個頭文件中,因此在此處僅給出定義代碼,實現代碼可在向量的具體實現裡找到
由此而言,一個基本的向量便封裝完成,由於采用的是模板的方式進行封裝,因此該方式可以容納任何數據類型,這其中則不論是int、char、float甚至是Student等自定義類型,而容器類對象,只負責在內存中存儲。這便是STL的核心思想!
在C++中,我們知道,都是有main函數作為入口,那麼我們便可以在main函數中對類進行測試。與其他平台和語言不同,JAVA等語言擁有豐富的庫提供它進行單元測試,而C++則相對較少,一次使用main函數作為測試是一項常見的方式!
在main函數中,需要對每個函數進行覆蓋,從而實現對類的測試,達到較為准確的測試效果。
//
// main.cpp
// Array
//
// Created by 鄒智鵬 on 16/7/3.
// Copyright ? 2016年 Frank. All rights reserved.
//
#include
#include "Array.hpp"
#include "Iterator.hpp"
using namespace ZTemplate;
int main(int argc, const char * argv[]) {
// insert code here...
ZArray *array = new ZArray(6);
array->push_back(5);
array->push_back(6);
Iterator &it = array->iterator();
while (it.hasNext()) {
std::cout << it.data() << std::endl;
it++;// 在重載中,有使用next()方法,則測試該方法即可
}
std::cout << " length: " << array->length() << std::endl;
std::cout << array->at(0) << std::endl;
std::cout << array->pop(0) << " after length:" << array->length() << std::endl; // 在pop中,有對remove的調用,則只覆蓋該方法即可
return 0;
}
以上為測試的簡單代碼,匹配了int型,可以看到正確的結果!
在進行自定義類型的測試
//
// main.cpp
// Array
//
// Created by 鄒智鵬 on 16/7/3.
// Copyright ? 2016年 Frank. All rights reserved.
//
#include
#include "Array.hpp"
#include "Iterator.hpp"
using namespace ZTemplate;
class Student {
public:
Student(){name = "", age = 0;}
Student(std::string n, int a){name = n, age = a;}
Student(const Student &stu){name = stu.name, age = stu.age;}
void display() const{std::cout << "name:" << name << "age:" << age << std::endl;}
private:
std::string name;
int age;
};
int main(int argc, const char * argv[]) {
// insert code here...
ZArray *array = new ZArray;
array->push_back(Student("zz", 19));
array->push_back(Student("ee", 20));
Iterator &it = array->iterator();
while (it.hasNext()) {
it.data().display();
it++;// 在重載中,有使用next()方法,則測試該方法即可
}
std::cout << " length: " << array->length() << std::endl;
array->at(0).display();
array->pop(0).display();
std::cout << " after length:" << array->length() << std::endl; // 在pop中,有對remove的調用,則只覆蓋該方法即可
return 0;
}
結果仍然符合預期
到此,你的一個自定義類的封裝過程已經基本結束,在這裡,你可以基本的看到面向對象的基本雛形,而結束之後,更應該考慮該方式是否合理,是否有更優秀的方式去設計!