您的位置:首页 > 其它

第11章 定义抽象数据类型

2010-04-02 14:27 246 查看
定义抽象类型的目标:让自定义类型如何内置类型一样易用,其使用符合一般习惯。

确定类接口:当我们准备要编写一个抽象的时候(这里指的是使用类概念),我们首先要确定这个抽象的接口。我们的抽象类型是提供给客户(可能是自己)使用的,那么其接口应该从客户的使用上来考虑。例如,我们准备写一个窗口抽象,那么我们很容易就能够想到,客户可能对这个窗口进行移动、改变大小、显示、隐藏、关闭等等,这些动词就应该列入我们的接口候选列表。

构造、析构、复制和赋值:我们要实例化一个类,就需要对这个类对象进行构造。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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: