第11章 定义抽象数据类型
2010-04-02 14:27
246 查看
定义抽象类型的目标:让自定义类型如何内置类型一样易用,其使用符合一般习惯。
确定类接口:当我们准备要编写一个抽象的时候(这里指的是使用类概念),我们首先要确定这个抽象的接口。我们的抽象类型是提供给客户(可能是自己)使用的,那么其接口应该从客户的使用上来考虑。例如,我们准备写一个窗口抽象,那么我们很容易就能够想到,客户可能对这个窗口进行移动、改变大小、显示、隐藏、关闭等等,这些动词就应该列入我们的接口候选列表。
构造、析构、复制和赋值:我们要实例化一个类,就需要对这个类对象进行构造。C++提供了构造函数语义来让我们可以对类对象的构造进行控制。一个类结束其生命周期的时候,我们可能要进行一些处理(比如释放资源等),C++也为我们提供了析构函数语义,让我们在对象生命结束时做一些事情。同样的,对象的复制和赋值等操作也能够在我们的掌握当中,这也得归功于C++提供了复制构造函数语义和重载操作符语义。
默认行为:如果我们不自行编写构造、析构、复制和赋值函数,编译器会为我们生成默认的一些函数,进行一些默认的处理。当然,我们必须得了解C++默认的构造、析构、复制和赋值函数都是如何工作的,以便在其不能满足我们要求的时候我们自行实现。
惯用法:这里提出一个C++惯用法,是关于析构、复制和赋值的。经验得出,析构、复制和赋值这三个行为关系非常密切,所以当C++程序员自行实现了其中某一个的时候,另外两个通常都应该一起实现。
复制和赋值:复制(拷贝构造)和赋值是完全不同的两个概念,但往往很多人容易将二者混淆。复制在这里指的是一种构造语义,是通过另一个已经存在的老对象来构造一个新的对象,新的对象是老对象的一个副本(内部数据值相同)。而赋值不是构造语义,顾名思义,赋值即赋予值的意思,也即使说是给对象赋予新的值,它的前提就是被赋值的对象已经存在。
本章教材以实现一个简化的Vec为例,阐述了如何去定义抽象数据类型。学习这一章,大家应该学会在定义抽象数据类型的时候,不要以实现为导向去设计类,而应该以用户使用为导向结合类本身应该具备的语义去设计。之所以提出这一点,也是我们通过课堂上大家的反馈,发现大家都习惯于在设计时就过多的考虑实现,导致设计出来的抽象不易用,不符合语义等等。
教材中代码:
确定类接口:当我们准备要编写一个抽象的时候(这里指的是使用类概念),我们首先要确定这个抽象的接口。我们的抽象类型是提供给客户(可能是自己)使用的,那么其接口应该从客户的使用上来考虑。例如,我们准备写一个窗口抽象,那么我们很容易就能够想到,客户可能对这个窗口进行移动、改变大小、显示、隐藏、关闭等等,这些动词就应该列入我们的接口候选列表。
构造、析构、复制和赋值:我们要实例化一个类,就需要对这个类对象进行构造。C++提供了构造函数语义来让我们可以对类对象的构造进行控制。一个类结束其生命周期的时候,我们可能要进行一些处理(比如释放资源等),C++也为我们提供了析构函数语义,让我们在对象生命结束时做一些事情。同样的,对象的复制和赋值等操作也能够在我们的掌握当中,这也得归功于C++提供了复制构造函数语义和重载操作符语义。
默认行为:如果我们不自行编写构造、析构、复制和赋值函数,编译器会为我们生成默认的一些函数,进行一些默认的处理。当然,我们必须得了解C++默认的构造、析构、复制和赋值函数都是如何工作的,以便在其不能满足我们要求的时候我们自行实现。
惯用法:这里提出一个C++惯用法,是关于析构、复制和赋值的。经验得出,析构、复制和赋值这三个行为关系非常密切,所以当C++程序员自行实现了其中某一个的时候,另外两个通常都应该一起实现。
复制和赋值:复制(拷贝构造)和赋值是完全不同的两个概念,但往往很多人容易将二者混淆。复制在这里指的是一种构造语义,是通过另一个已经存在的老对象来构造一个新的对象,新的对象是老对象的一个副本(内部数据值相同)。而赋值不是构造语义,顾名思义,赋值即赋予值的意思,也即使说是给对象赋予新的值,它的前提就是被赋值的对象已经存在。
本章教材以实现一个简化的Vec为例,阐述了如何去定义抽象数据类型。学习这一章,大家应该学会在定义抽象数据类型的时候,不要以实现为导向去设计类,而应该以用户使用为导向结合类本身应该具备的语义去设计。之所以提出这一点,也是我们通过课堂上大家的反馈,发现大家都习惯于在设计时就过多的考虑实现,导致设计出来的抽象不易用,不符合语义等等。
教材中代码:
// Vec.h #ifndef VEC_H #define VEC_H #include <memory> template<typename T> class Vec{ public: typedef T* iterator; typedef const T* const_iterator; typedef size_t size_type; typedef T value_type; private: iterator data_; // Vec中的首元素 iterator avail_; // Vec中末元素后面一个元素 iterator limit_; // 新分配内存中末元素后面一个元素 // 内存分配工具 std::allocator<T> alloc_; public: Vec() {create_();} explicit Vec(size_type n, const T& val = T()){create_(n, val);} Vec(const Vec& v){create_(v.begin(), v.end());} Vec& operator=(const Vec&); ~Vec(){uncreate_();} public: size_type size() const{return limit_-data_;} T& operator[](size_type i){return data_[i];} const T& operator[](size_type i) const{return data_[i];} iterator begin(){return data_;} const_iterator begin() const{return data_;} iterator end(){return avail_;} const_iterator end() const{return avail_;} void push_back(const T& val) { if (avail_ == limit_) { grow(); } unchecked_append(val); } private: // 为底层的数组分配控件并对它进行初始化 void create_(); void create_(size_type n, const T& val); void create_(const_iterator beg, const_iterator end); // 删除数组中的元素并释放其产用的内存 void uncreate_(); // 支持push_back的函数 void grow(); void unchecked_append(const T& val); }; template<typename T> Vec<T>& Vec<T>::operator =(const Vec& rhs) { if (&rhs != this) { uncreate_(); create_(rhs.begin(), rhs.end()); } return *this; } template<typename T> void Vec<T>::create_() { data_ = avail_ = limit_ = 0; } template<typename T> void Vec<T>::create_(size_type n, const T& val) { data_ = alloc_.allocate(n); limit_ = avail_ = data_ + n; std::uninitialized_fill(data_, limit_, val); } template<typename T> void Vec<T>::create_(const_iterator beg, const_iterator end) { data_ = alloc_.allocate(end-beg); limit_ = avail_ = std::uninitialized_copy(beg, end, data_) } template<typename T> void Vec<T>::uncreate_() { if (data_) { // (以相反的顺序)删除构造函数生成的元素 iterator iter = avail_; while (iter != data_) { alloc_.destroy(--iter); } // 返还占用的所有内存空间 alloc_.deallocate(data_, limit_-data_); } // 重置指针以表明Vec类型对象为空 data_ = limit_ = avail_ = 0; } template<typename T> void Vec<T>::grow() { // 在扩展对象大小的时候,为对象分配实际使用的两倍大小的内存空间 size_type newSize = max(2*(limit_-data_), ptrdiff_t(1)); // 分配新的内存空间并将已存在的对象元素内容复制到新内存中 iterator newData = alloc_.allocate(newSize); iterator newAvail = std::uninitialized_copy(data_, avail_, newData); // 返还原来的内存空间 uncreate_(); // 重置指针,使其指向新分配的内存空间 data_ = newData; avail_ = newAvail; limit_ = data_+newSize; } template<typename T> void Vec<T>::unchecked_append(const T& val) { alloc_.construct(avail_++, val); } #endif // VEC_H
相关文章推荐
- 第11章 定义抽象数据类型(续)
- 【足迹C++primer】15、定义抽象数据类型
- bo1-1.cpp 抽象数据类型Triplet和ElemType(由c1-1.h定义)的基本操作(8个)
- 7.1定义抽象数据类型
- 抽象数据类型线性表的定义—评论为顺序表
- 第11章第15题定义代码(h) (Powered by biggates)
- 第02话:线性表的抽象数据类型ADT定义
- 抽象数据类型线性表的定义—链表操作
- 抽象数据类型线性表的定义—静态单链表
- C++ Primer读书笔记7.1:定义抽象数据类型
- 小议数据结构中抽象数据类型struct的定义
- 第11章 动态方法调用和使用通配符定义action
- 抽象数据类型线性表的定义
- C++ 标准库定义了更高级的抽象数据类型
- 抽象数据类型三元组的定义
- 定义抽象数据类型
- accelerated cpp chapter 11 定义抽象数据类型
- (二)二叉树的抽象数据类型定义及遍历
- 抽象数据类型定义(ADT)
- 通过Java实现单链表来透彻理解抽象数据类型的定义和应用