C++封装向量-线性表
来源:互联网 发布:网络广告代理公司多吗 编辑:程序博客网 时间:2024/05/29 16:33
封装前的考虑
在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 <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;}
结果仍然符合预期
反思你的封装过程
到此,你的一个自定义类的封装过程已经基本结束,在这里,你可以基本的看到面向对象的基本雏形,而结束之后,更应该考虑该方式是否合理,是否有更优秀的方式去设计!
- C++封装向量-线性表
- 用C语言封装的线性表
- 2.向量Vector(线性表)
- C++实现向量构造的线性表
- 线性代基础理论:向量
- 线性支持向量机
- 向量多元线性回归
- 线性支持向量机
- 线性支持向量机
- 自已封装的线性表(是线性存储)
- 线性表--数组描述(封装类)
- 线性表——1. 顺序表(向量)
- 关于两个向量组的线性无关与表出问题
- 线性表的顺序储存结构(向量 )(一)
- 线性表(C++)
- 数据结构-线性表 (C++)
- Objective-C 线性表
- 数据结构线性表c
- 无责任共享 Coursera、Udacity 等课程视频【百度云】
- Windows Server使用python实现自动化运维——开启系统更新服务
- 解决ubuntu系统中文拼音输入法输入错误的问题
- fzu2141(二分图着色)
- 快速排序
- C++封装向量-线性表
- HTML5+规范:Geolocation(管理设备位置信息)
- 安卓中测试用例
- 浅谈Android多屏幕的事
- CODE[VS] 1670 无穷的序列 二分答案水法
- CrackingtheCodeInterview之树与图
- Jsp ssh
- Mybatis的注解应用之关系映射
- BTrace使用总结