C++封装向量-线性表

来源:互联网 发布:网络广告代理公司多吗 编辑:程序博客网 时间:2024/05/29 16:33

封装前的考虑

在C++中有很丰富的库,当属STL模板,STL的设计和优化都为我们提供了应有的功能。然而对于新手而言,尝试进行一个封装,会使得自己更加熟悉面向对象。

面向对象三大特性:封装、继承、多态。这也是面向对对象语言相对面向过程而言,最大的优势和特点。面向对象使得程序更加利于维护,让设计人员更加关注设计,要想真正的理解面向对象的特性,则必须要清楚和掌握这三大规律。

在C++中,STL提供了Vector类,表示向量,其本质则是线性表的实现,并且可以在内部实现自动扩容,并且借助迭代器,可以很方便很快速的对表中元素进行访问和遍历。

因此我们手动封装的线性表,可以有以下的功能:

  1. 自动扩容
  2. 迭代器访问元素
  3. 下标式快速访问元素值
  4. 移除元素
  5. 增加元素
  6. 长度管理

上述这些功能,都是需要一个向量所需要提供的功能,并且基于面向对象的设计而言,采用迭代器模式的设计,可以很好的减小容器对象与数据之间的紧密耦合,通过迭代器去遍历向量表,可以很大程度的增加遍历的安全性和便利性。

在该设计中,可进行三个类的设计: 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 <stdio.h>#include "Iterator.hpp"namespace ZTemplate {    typedef unsigned int z_size;    // 用于表示大小,为无符号整形    typedef long rank;              // 用于表示秩    template<class T>    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<T> &iterator() = 0;        /**         * 重载访问器         * @param pos 元素位置         * @return 返回元素引用         */        virtual T& operator[](const rank pos) = 0;    protected:    };}template<class T>bool ZTemplate::Sequence<T>::remove(T &val1, __FUNC_COMPARE_ compare) {    Iterator<T> &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 <stdio.h>namespace ZTemplate {    template<class T>    class Iterator {    public:        /**         * 默认构造函数         */        Iterator<T>(){}        /**         * 下一个元素         * @return 返回下一个迭代器         */        virtual Iterator<T> &next() = 0;        /**         * 获取迭代器值         * @return 返回值         */        virtual const T data() const = 0;        /**         * 是否含有下一个元素         * @return 返回是否有后续元素         */        virtual bool hasNext() = 0;        /**         * 重载++         */        virtual Iterator<T>& operator++() = 0;        virtual Iterator<T>& 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 <stdio.h>#include <cstring>#include "Sequence.hpp"namespace ZTemplate {    template<class T>    class ZArrayIterator;    template<class T>    class ZArray : public Sequence<T>{    public:        /**         * 默认构造         */        ZArray<T>();        /**         * 根据容量构造向量         * @param capacity 容量         */        ZArray<T>(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<T> &iterator();        /**         * 返回长度信息         * @return 返回数组的有效长度         */        z_size length()const{return _length;}        /**         * 重载访问器         * @param pos 元素位置         * @return 返回元素引用         */        T& operator[](const rank pos);        /**         * 析构函数         */        ~ZArray<T>();        friend class ZArrayIterator<T>;    protected:        T *_array; // 实际存储空间        z_size _length; // 数组长度        z_size capacity; // 最大容量        Iterator<T> *_iterator; // 迭代器        /**         * 缩容         */        T *shink();        /**         * 扩容         */        T *expand();    };    /**迭代器类*/    template<class T1>    class ZArrayIterator : public Iterator<T1> {    protected:        rank pointTo;// 当前游标        ZArray<T1> *_array;    public:        /**         * 构造函数         * @param array 用于遍历数组         */        ZArrayIterator<T1>(ZArray<T1> &arr):Iterator<T1>(){this->_array = &arr;}        /**         * 下一个元素         * @return 返回下一个迭代器         */        virtual Iterator<T1> &next();        /**         * 获取迭代器值         * @return 返回值         */        virtual const T1 data() const;        /**         * 是否含有下一个元素         * @return 返回是否有后续元素         */        virtual bool hasNext();        /**         * 重置前置++         */        virtual Iterator<T1>& operator++();        /**         * 重载后置++         */        virtual Iterator<T1>& operator++(int);    };}template<class T>ZTemplate::ZArray<T>::ZArray():Sequence<T>() {    static z_size size = 5;    _array = new T[size];    capacity = size;    _length = 0;    _iterator = new ZArrayIterator<T>(*this);}template<class T>ZTemplate::ZArray<T>::ZArray(z_size capacity) {    _array = new T[capacity];    this->capacity = capacity;    _length = 0;    _iterator = new ZArrayIterator<T>(*this);}template<class T>void ZTemplate::ZArray<T>::push_back(const T &val) {    if (_length == capacity) {        _array = expand();    }    _array[_length] = val;    _length++;}template<class T>T ZTemplate::ZArray<T>::pop(const rank pos) {    T val =  _array[pos]; // 要返回的值    remove(pos);    return val;}template<class T>const T& ZTemplate::ZArray<T>::at(const rank pos) const{    return _array[pos];}template<class T>bool ZTemplate::ZArray<T>::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<class T>T* ZTemplate::ZArray<T>::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<class T>T * ZTemplate::ZArray<T>::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<class T>ZTemplate::ZArray<T>::~ZArray<T>() {    delete _array;    _array = nullptr;    _length = 0;    capacity = 0;}template<class T>T & ZTemplate::ZArray<T>::operator[](const rank pos) {    return _array[pos];}template<class T1>ZTemplate::Iterator<T1>& ZTemplate::ZArrayIterator<T1>::next() {    pointTo++;    return *this;}template<class T1>bool ZTemplate::ZArrayIterator<T1>::hasNext() {    return pointTo < _array->length();}template<class T1>const T1 ZTemplate::ZArrayIterator<T1>::data() const {    return _array->_array[pointTo];}template<class T>ZTemplate::Iterator<T> &ZTemplate::ZArray<T>::iterator() {    return *(new ZArrayIterator<T>(*this));}template<class T1>ZTemplate::Iterator<T1> &ZTemplate::ZArrayIterator<T1>::operator++() {    next();    return *this;}template<class T1>ZTemplate::Iterator<T1> &ZTemplate::ZArrayIterator<T1>::operator++(int i) {    ZTemplate::Iterator<T1> &it = *(new ZArrayIterator<T1>(*this));    next();    return it;}#endif /* Array_hpp */
  • 对向量迭代器的具体实现
/**迭代器类*/    template<class T1>    class ZArrayIterator : public Iterator<T1> {    protected:        rank pointTo;// 当前游标        ZArray<T1> *_array;    public:        /**         * 构造函数         * @param array 用于遍历数组         */        ZArrayIterator<T1>(ZArray<T1> &arr):Iterator<T1>(){this->_array = &arr;}        /**         * 下一个元素         * @return 返回下一个迭代器         */        virtual Iterator<T1> &next();        /**         * 获取迭代器值         * @return 返回值         */        virtual const T1 data() const;        /**         * 是否含有下一个元素         * @return 返回是否有后续元素         */        virtual bool hasNext();        /**         * 重置前置++         */        virtual Iterator<T1>& operator++();        /**         * 重载后置++         */        virtual Iterator<T1>& 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 <iostream>#include "Array.hpp"#include "Iterator.hpp"using namespace ZTemplate;int main(int argc, const char * argv[]) {    // insert code here...    ZArray<int> *array = new ZArray<int>(6);    array->push_back(5);    array->push_back(6);    Iterator<int> &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 <iostream>#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<Student> *array = new ZArray<Student>;    array->push_back(Student("zz", 19));    array->push_back(Student("ee", 20));    Iterator<Student> &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;}

结果仍然符合预期
自定义类型测试

反思你的封装过程

到此,你的一个自定义类的封装过程已经基本结束,在这里,你可以基本的看到面向对象的基本雏形,而结束之后,更应该考虑该方式是否合理,是否有更优秀的方式去设计!

2 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 英雄联盟自动更新失败怎么办 英雄联盟老出错怎么办? lol下载0kb怎么办 手机md5校验失败怎么办 王者荣耀占内存怎么办 信用卡被暂停使用怎么办 宝宝爱涂鸦怎么办呢 大良老板欠薪怎么办 投影仪流明很低怎么办 户外涂鸦没有墙怎么办 word突然未响应怎么办 文档突然未响应怎么办 pr不自己播放怎么办 笔记本电脑空格键打字失灵怎么办 小孩受凉打喷嚏流鼻涕怎么办 头疼打喷嚏流鼻涕鼻塞怎么办 头疼打喷嚏想吐怎么办 感冒鼻塞头疼打喷嚏怎么办 孕期打喷嚏漏尿怎么办 孕妇打喷嚏漏尿怎么办 打喷嚏停不下来怎么办 鼻炎不停的打喷嚏怎么办 感冒了鼻子痒怎么办 怀孕鼻炎严重了怎么办 鼻炎不停打喷嚏流鼻涕怎么办 鼻子里面干痒怎么办 有鼻炎老打喷嚏怎么办 鼻子喷嚏打不停怎么办 鼻子痒想打喷嚏怎么办 鼻痒打喷嚏流鼻涕怎么办 原配找到公司了怎么办 西北游不吃羊肉怎么办 水培植物冬天该怎么办 车里味道太大怎么办 同房后有异味怎么办 房间里有异味怎么办 身上有孤臭味怎么办 炉灶打不着火怎么办 檀香木香味小了怎么办 郁金香种球发霉怎么办 烧到小天真香该怎么办