程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C++封裝向量

C++封裝向量

編輯:關於C++

封裝前的考慮

在C++中有很豐富的庫,當屬STL模板,STL的設計和優化都為我們提供了應有的功能。然而對於新手而言,嘗試進行一個封裝,會使得自己更加熟悉面向對象。

面向對象三大特性:封裝、繼承、多態。這也是面向對對象語言相對面向過程而言,最大的優勢和特點。面向對象使得程序更加利於維護,讓設計人員更加關注設計,要想真正的理解面向對象的特性,則必須要清楚和掌握這三大規律。

在C++中,STL提供了Vector類,表示向量,其本質則是線性表的實現,並且可以在內部實現自動擴容,並且借助迭代器,可以很方便很快速的對表中元素進行訪問和遍歷。

因此我們手動封裝的線性表,可以有以下的功能:

自動擴容 迭代器訪問元素 下標式快速訪問元素值 移除元素 增加元素 長度管理

上述這些功能,都是需要一個向量所需要提供的功能,並且基於面向對象的設計而言,采用迭代器模式的設計,可以很好的減小容器對象與數據之間的緊密耦合,通過迭代器去遍歷向量表,可以很大程度的增加遍歷的安全性和便利性。

在該設計中,可進行三個類的設計: Sequeence 抽象基類,ZArray類,Iterator 抽象基類, ZArrayIterator類,其中兩個抽象基類中分別提供了序列類的基本操作,增刪查改等,Iterator基本定義了迭代器的方法接口,可以很好的實現多態,並且可以擁有很好的擴展性,因為對序列數據容器而言,其邏輯結構可以是連續的向量式的,也可以是鏈式的,由此他們之間的遍歷方式都不同,訪問方式也不同,因此為了可擴展性,可以進行抽象,使得迭代器和序列容器進行交互,而無需使用具體的子類,並且可以很好的進行擴展。那麼在這裡,面向對向的特性則充分包含了進去,封裝是完好類設計的基礎,繼承則是擴展的必經之路,多態則是在面向抽象編程的基礎。


值得注意的是,由於在C++中容器類都采用模板的方式去進行封裝,由此需要注意的是,最好將.h的定義和.cpp的定義為一個文件中,否則在某些編譯環境中可能造成link錯誤!

學會如何去抽象

首先我們進行Sequence類的抽象定義
//
//  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;
}

結果仍然符合預期
自定義類型測試

反思你的封裝過程

到此,你的一個自定義類的封裝過程已經基本結束,在這裡,你可以基本的看到面向對象的基本雛形,而結束之後,更應該考慮該方式是否合理,是否有更優秀的方式去設計!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved