1 设计类
开始设计类时,通常要首先确定要在什么类中提供什么样的接口。精确确定接口的一种方式是研究一下类的使用者将用我们所编写的类写什么程序。
由于我们要设计与标准库的向量相同的功能。因此我们可以以向量类的使用作为模仿对象。
比如向量可以动态增加元素、索引访问元素、数据成员可以存储不同的内建类型等。
2 实现Vec类
2.1 类的类型
因为类中要存储几种不同的数据成员,因此我们使用模版类来实现。
template <class T> class Vec{
public:
//接口
private:
//实现
}
2.2 数据成员
由于类中要实现begin、end和size等函数的功能,因此需要首先保存元素的首地址、元素后面的地址以及元素的个数。
这三个数据知道任意两个就能推出第三个。为了方便后面内存管理的指针使用,这里选择了只保留数组的首地址和末元素后面的地址指针,然后计算出数组的大小。
这里进一步完善Vec类:
template <class T> class Vec{
public:
//接口
private:
T* data;
T* limit;
}
在使用T类型时,编译器会使用用户在生成Vec时提供的参数代替T。该类型只有在一个Vec的定义实例化时菜户被确定。
2.3 内存分配
2.3.1 如何分配内存(预分配内存)
现向一个Vec类添加一个元素,由于vec类对象中的元素增加了一个(这里内存大小是固定,每次只能容纳下当前的元素),因此需要为对象重新分配新的内存,然后将原内存中的元素复制到当前新的内存中,然后构造一个指向新末端元素的后面一个迭代器。
如果频繁增加元素,那么开销将会非常大。
因此将采用一种经典的方法,为程序分配比实际更多的内存,只有在使用完所有预分配的内存时,才可以申请更多的内存。
即,只要向类对象要求分配更多的内存时,我们都为类对象分配当前使用的空间的两倍的内存空间。
例如,我们创建一个包含10个元素的Vec类对象,然后向类对象中添加元素(调用push_back函数),这个函数将会分配20个元素的内存空间。它会将现存的10个元素复制到新分配的内存的前一半空间,并为接下来的第一个元素空间进行初始化。
这样的预分配需要我们增加元素指针。
原来的“末指针”指向新分配内存空间的末尾后面元素;
另一个新的“末指针”指向构造元素的末元素后面的那个元素(也就是新分配内存后的访问的第一个元素)。
具体如下图所示,原来的“末指针”(limit),新的“末指针”(avail)。
这里进一步完善Vec类:
template <class T> class Vec{
public:
//接口
private:
T* data;
T* avail;//新增
T* limit;
}
2.3.2 使用库函数实现内存分配
2.3.2.1 思想
我们可能希望用newT[n]为Vec分配空间,其中n为要分配内存的数目。但是,new T[n]不仅分配内存空间,还会运行T的构造函数来为每个元素进行默认初始化。
如果打算使用new T[n],就必须满足:只有在T具有默认的构造函数时才能创建一个Vec<T>。
而这有悖于为用户提供尽可能大的弹性的初衷。
由于标准的向量vector类中,没有这样的限制,因此我们编写的类也希望没有这样的限制。这就要用到库函数提供的内存分配类。
另外,如果我们要使用自己提供的数据进行初始化Vec类对象的元素,实际上它会进行两次初始化:
- 1 一次是new自己进行的,使用T:T()构造函数为一个类型为T的数组中的每个元素进行初始化;
- 2 另一次是将用户提供的数值赋给Vec类型对象的元素时。
但是如果我们使用前面的预分配的方法(分配我们实际需要的两倍的空间),没有必要对这些额外的元素(多分配的空间里的元素)进行初始化。这些空间只有我们新增加元素(使用push_back函数)时,才会被使用。
不用new,总结起来就是两点:
- 1 为用户提供尽可能大的弹性的初衷;
- 2 当使用预分配内存,没必要对多分配空间里的元素进行初始化。
2.3.2.2 实现
2.3.2.2.1 库函数准备
由于关于内存的性质太复杂多变,没有必要将特性固定在语言中,因此我们使用C++专门设计以支持灵活内存管理的一些类来管理内存。
例如计算机中有非常多种类的内存。一台计算机可能有几种不同速度的内存在使用。计算机上的内存可能是具有特殊用途的,像图形缓冲内存或共享内存等,有些内存可能在断电后仍然保持记忆的,由于用户可能想要分配和管理其中任何一种内存,因此最好将如何分配和管理的工作留给库函数。
标准库并不需要支持所有的内存,相反它提供了一种功能来管理内存,还有一个同一的内存管理接口。
在<memory>头文件中,提供了一个名为allocator<T>的类,它可以分配一块预备用于存储T类型对象但是尚未被初始化的内存快,并返回一个指向这块内存块中的头元素的指针。
这样的指针非常危险,由于指针的类型表明它们指向类型对象,但是这些内存块中并没有存储实际的对象。
allocator的模版函数如下:
template<class T> class allocator{
public:
T* allocate(size_t);//分配未初始化内存块
void deallocate(T*, size_t);//释放未初始化内存块
void construct(T*, const T&);//未初始化内存块构造单个对象
void destory(T*);//删除参数所指的对象
//...
}
其中:<cstddef>头文件中的size_t类型(无符号整型)表示数组的大小,size_t类型的大小可以装在任何对象。
这里我们仅研究与创建Vec类有关的成员函数以及非成员函数。
-
1 allocate成员函数:用于分配一块被指定了类型但却未被初始化的内存块,它足以存储相应类型对象的元素。
- 它被称为被指定了类型的内存块,因为它被用于存储类型为T的值,并且可以通过使用一个T*类型的指针来得到它的地址。但是,它未初始化,这个内存块中没有构造任何实际的对象。
-
2 deallocate成员函数:用于释放未被初始化的内存,它的两个参数分别为:allocate函数已分配的指针;另一个是指出该内存大小中分配了多少元素。
-
3 construct成员函数:在尚未被初始化的内存块区域中构造单个对象。它的两个参数分别是:一个是allocate函数已分配的内存指针;另一个是用于复制到该内存块的值。
-
4 destory成员函数:删除它的参数所指的类型T的对象。
与allocator类有关的非成员函数:
//一个区间的值复制到另一个区间
template<class In, calss For> For uninitalized_copy(In, In, For);
//用值来填充一个区间
template<class For, class T>
void uninitialized_fill(For, For, const T&);
其中:In是一个输入迭代器类型,For是一个前向迭代器类型(顺序读写访问迭代器类型)。因为构造新对象不只是为它分配内存,所以For必须是前向迭代器类型,而不是输出迭代器类型。
这两个函数用于在allocate所分配的内存块中构造并初始化新的对象。
uninitalized_copy函数:类似与标准库的中copy函数,用于将前两个参数指针所指向的内存块区间中的值复制到第三个参数指针所指向的目标内存块中。
uninitialized_fill函数:根据需要构造其第三个参数尽可能多的副本,以填充前两个参数提供的内存块。
2.3.2.2.2 类不变式
为了建立一个有些的Vec类型对象,我们必须始终满足以下4个条件:
- 1 如果对象存在元素,data指向对象数组的首元素,否则为零;
- 2 d a t a ≤ a v a i l ≤ l i m i t data \leq avail \leq limit data≤avail≤limit;
- 3 在 [ d a t a , a v a i l ) [data,avail) [data,avail)区间内的元素被初始化;
- 4 在 [ a v a i l , l i m i t ) [avail, limit) [avail,limit) 区间内的元素不会初始化。
这四个条件叫做类不变式(class invariant)。一旦创建了一个类对象,总要建立起类不变式条件。
如果满足了4个条件,只要3个成员函数不改变这4个条件,那么这个规律就永远成立。
类的所有公有成员都不能打破这一不变式。
2.4 成员函数
2.4.1 构造函数
默认构造函数:
template<class T>
void Vec<T>::create(){
data = avail = limit = 0;
}
Vec(){ create();}
带参构造函数:
带两个参数:长度、初值
template<calss T>
void Vec<T>::create(size_type n, const T& val){
data = alloc.allocate(n);
limit = avail = data + n;
uninitialized_fill(data, limit, val);
}
explict Vec(size_type n, const T& t = T()){
create(n, t);
}
复制构造函数:
带两个参数:头指针、尾指针
template<class T>
void Vec<T>::create(const_iterator i, const_iterator j){
data = alloc.allocate(j -i);
limit = avail = uninitialized_copy(i, j, data);
}
Vec(const Vec& v){
create(v.begin(), v.end());
}
带参构造函数:
带两个迭代器参数的
template<class T> class Vec{
public:
//带两个迭代器参数的构造函数
template <class In> Vec(In b, In e){create(b, e);}
private:
template <class In> void create(In, In);
}
定义:
template <class T>
template <class In>
void Vec<T>::create(In b, In e) {
data = alloc.allocate(e - b);
limit = avail = std::uninitialized_copy(b, e, data);
}
2.4.2 析构函数(destructor)
一个在局域范围里被创建的对象在它的生存范围以外,就会被自动删除;而动态分配的内存的对象,则只有在我们使用delete函数删除它时才会被删除。
对于Vec类我们在构造函数中为其分配了内存,因此必须要在析构函数中释放内存。
如果使用默认析构函数,会仅删除对象的指针,而删除一个指针不会释放指针对象占用的内存空间,最终结果导致内存泄露。
template<class T>
void Vec<T>::uncreate(){
if(data){//以相反的顺序,删除构造函数生成的元素
iterator it = avail;
while(it != data){
alloc.destory(--t);
}
alloc.deallocate(data, limit - data);
}
//重置指针以表明Vec类型对象为空
data = limit = avail = 0;
}
~Vec(){uncreate();}
2.4.3 复制构造函数(destructor)
当类中有指针成员时,如果复制指针的值,则被复制的对象和复制后的对象都指向内存中的同一个数据。这样会导致任何对象的数据元素的改动都会影响另一个对象的值。
解决的办法,就是通过值传递将对象作为参数传递过去,这样在复制对象中的操作不会影响原本对象中的值。
因此有复制构造函数:
template<class T> class Vec{
public:
Vec(const Vec& c){create(v.begin(), v.end());}
}
2.4.5 重载运算符函数
2.4.5.1 索引运算符函数
索引运算符在数组中定位正确的元素位置并返回该元素的一个引用。通过返回的引用,可以修改Vec类型对象中所存储的数据。
返回类型为引用而不是值是为了避免容器中的对象非常大时,对它进行复制,那样不但会造成时间上的浪费,更会影响运行速度。
//读写
T& operator[](size_type i){return data[i];}//必须是成员函数
//只读
const T& operator[](size_type i) const {return data[i];}
2.4.5.2 赋值运算符
进行赋值时要进行自我赋值判断,如果不是自我赋值,才删除原对象并释放内存,然后复制新对象。
如果去掉这个判断,会造成将左操作数对象的元素删除并释放其占用的内存的同时,由于左右操作数指向同一对象,导致右操作数同时被删除。但还要将右操作对象复制,这将会带来灾难。
常用this判断,this关键词只在成员函数内部才有效,代表指向函数操作的对象的指针。例如在Vec::operator=函数里,this的类型为Vec*。对于二元操作来说,如赋值操作,this总是指向左操作数。
在赋值操作中,我们返回一个指向 表达式做操作数对象的一个引用调用,该对象的生存周期大于赋值操作,保证了函数返回的时候不被删除。
继续完善Vec类:
template<class T > class Vec{
public:
//在模版文件的范围内,C++允许我们忽略器具体类型名称。
Vec& operator=(const Vec&);//必须是成员函数
}
//实现
//一旦前面指定了我们定义一个Vec<T>类型的成员函数,后面就不需要重复使用这个定语了。
template<class T>
Vec<T>& Vec<T>::operator=(const Vec& rhs){
if(&rhs != this){//进行字符赋值判断
uncreate();//删除运算符左测的数组
create(rhs.begin(), rhs.end());//从右侧元素复制到左侧
}
returnn *this;
}
2.4.6 push_back成员函数
如果当前对象的内存不够插入新的元素,那么就需要重新分配足够的内存,以便使其至少再多存储一倍的元素。
下面的grow函数,就是实现这样的功能。
template<class T> void Vec<<T>::grow(){
//扩展对象大小,并为对象分配实际使用的两倍大小的空间
size_type new_size = max(2*(limit - data),ptrdiff_t(1));
//分配新的内存空间并将已存在的对象元素内容复制到新内存中
iterator new_data = alloc.allocate(new_size);
iterator new_avail = uninitialized_copy(data,avail,new_date)
//返回原来的内存空间
uncreate();
//重置指针,使其指向新分配的内存空间
data = new_data;
avail = new_avail;
limit = data + new_size;
}
在新分配完内存空间后,就是插入元素。
template<class T>
void Vec<T>::unchecked_append(const T& val){
alloc.construct(avail++, val);
}
于是push_back函数就可以写成:
template<class T>
class Vec{
public:
void push_back(const T& t){
if(avail = = limit)
grow();
unchecked_append(t);
}
}
由于grow函数会产生一个avail指针指向一个尚未被初始化的内存空间,但是我们在调用grow函数后,马上调用unchecked_append函数,这样avail指针就指向了一个有效值。
2.4.6 clear成员函数
倒着删除类对象中的值,
template <class T>
void Vec<T>::destory() {
iterator it = avail;
while(it != data){
alloc.destroy(--it);
}
avail = data;
}
继续完善Vec类:
template<class T>
class Vec{
public:
void clear(){
destory();
}
private:
void destory();
}
2.4.7 erase成员函数
删除单个元素:(判断对象非空以及删除位置有效)
首先删除对象中的元素,然后把该位置后一个的元素值复制到当前未初始化元素的位置,后一个元素值清空,然后这样依次复制直到当前位置等于avail为止,最后重置avail指针。
template <class T>
typename Vec<T>::iterator Vec<T>::destory(Vec::iterator pos) {
if(data && pos < avail && pos >= data){
alloc.destroy(pos);
iterator it = pos +1;
while(it != avail){
alloc.construct(pos++, *it++);
alloc.destroy(pos);
}
avail = pos;
}
return avail;
}
删除多个元素:(判断对象非空以及删除区间有效)
首先删除区间内对象中的元素,然后把区间后的元素依次移动到被删除的区间(每次移动后,都删除区间后面对应位置的元素的值)。
template <class T>
typename Vec<T>::iterator Vec<T>::destory(Vec<T>::iterator b, Vec<T>::iterator e) {
if(data && b < e && e < avail && b >= data){
iterator it = b;
while(it != e){
alloc.destroy(it++);
}
while(e != avail){
alloc.construct(b++, *e);
alloc.destroy(e++);
}
avail = b;
}
return avail;
}
继续完善Vec类:
template<class T>
class Vec{
public:
void erase(iterator pos){
destory(pos);
}
void erase(iterator b , iterator e){
destory(b, e);
}
private:
iterator destory(iterator);
iterator destory(iterator, iterator);
}
2.4.8 pop_back成员函数
把最后一个进入向量的元素删除:
template <class T>
void Vec<T>::pop() {
if(data){
alloc.destroy(--avail);
}
}
继续完善Vec类:
template<class T>
class Vec{
public:
void pop_back(){
pop();
}
private:
void pop();
}
2.4.9 empty成员函数
为空时返回fasle。
template<class T>
class Vec{
public:
bool empty() const{return !data;}
}
2.4.10 insert函数
难点在于重新分配内存后,data、avail、limit的值都会改变,因为重新分配了一块新的内存,因此地址也是新的。结局方法就是记录插入的位置到起始点的距离,这样无论地址怎么改变,只要起始地址加上距离就可以计算出插入点在新内存的地址。
在迭代器的位置插入单个元素
template <class T>
void Vec<T>::add(std::ptrdiff_t dis, const T& val){
if(dis < avail - data){
iterator e = avail;
iterator b = avail -1;
while(e != data + dis){
alloc.construct(e--, *b--);
alloc.destroy(b);
}
alloc.construct(data + dis, val);
++avail;
}
}
继续完善Vec类:
template<class T>
class Vec{
public:
void insert(iterator it,const T& t){
std::ptrdiff_t dis = it - data;
if(avail == limit) grow();
add(dis, t);
}
private:
void add(std::ptrdiff_t,const T&);
}
插入区间内的元素
template <class T>
void Vec<T>::add(std::ptrdiff_t start,Vec<T>::const_iterator b, Vec<T>::const_iterator e) {
iterator pos = data + size_type(start);
if(size_type(start)> size()) throw std::domain_error("");
Vec<T>temp;
iterator insert_pos = pos;
for ( ; insert_pos != avail; ++insert_pos) {//原插入位置的后面部分暂存
temp.push_back(*insert_pos);
}
for (const_iterator it = b; it != e; ++it) {//覆盖插入元素
*insert_pos++ = *it;
}
for (const_iterator it = temp.begin(); it != temp.end(); ++it) {//把愿插入位置后面的部分重新插入
*insert_pos++ = *it;
}
//更新avail
avail = insert_pos;
// std::copy(pos, avail, temp);
// std::copy(temp.begin(),temp.end(),std::copy(b, e, pos));
}
继续完善Vec类:
template<class T>
class Vec{
public:
template <class In>
void insert(iterator out, In b, In e){
std::ptrdiff_t start = out - data;//插入位置
std::ptrdiff_t dis = e - b;
if(dis > 0){//插入区间合法
const size_type new_length = size() + size_type (dis);
size_type container = limit - data;//容器所有的长度包括未初始化
while(new_length >= container){
grow();
container = limit - data;
}
add(start, b, e);
}
}
private:
void add(std::ptrdiff_t, const_iterator, const_iterator);
}
2.4.11 assign函数:
复制一个迭代器中的内容到向量
template <class T>
template <class In>
void Vec<T>::assign(In b, In e) {
uncreate();
create(b, e);
}
声明:
template<class T> class Vec{
public:
template <class In>
void assign(In, In);
}
2.4.12 print_vec成员函数(类中没有,重写了输出流)
把向量中的元素读到输出流中。
template<class T>
ostream& Vec<T>::print_vec(std::ostream &os) {
if(avail - data > 0){
const_iterator iter = data;
os << *iter++;
while(iter != avail){
os << " " << *iter++;
}
os << std::endl;
}
return os;
}
继续完善Vec类:
template<class T>
class Vec{
public:
std::ostream& print_vec(std::ostream&);
}
3 完整版示例
头文件:
#ifndef Vec_H
#define Vec_H
#include <iostream>
#include <cstddef>
#include <memory>
#include <algorithm>
template<class T> class Vec{
public:
typedef T* iterator;
typedef const T* const_iterator;//迭代器
typedef size_t size_type;//容器长度
typedef T value_type;//值类型
typedef std::ptrdiff_t difference_type;//迭代器相减后的结果
typedef T& reference;//
typedef const T& const_reference;
//构造函数
Vec(){create();}
//可以显示的给val值,也可以使用T的默认构造函数来生成这个值
explicit Vec(std::size_t n, const T& val = T()){create(n, val);}
//复制构造函数
Vec(const Vec& v) { create(v.begin(), v.end());}
//带两个迭代器参数的构造函数
template <class In> Vec(In b, In e){create(b, e);}
//赋值运算符
Vec& operator=(const Vec&);//允许忽略具体类型的名称(因此没有显示声明返回类型名称)
//析构函数
~Vec(){ uncreate();}
//索引(返回引用,是为了避免容器的对象非常大时对它进行复制)
T& operator[](size_type i) { return data[i];}//读写
const T& operator[](size_type i) const { return data[i];}//只读
//动态增加数组
void push_back(const T& t){
if(avail == limit){
grow();
}
unchecked_append(t);
}
//清空
void clear(){
destory();
}
//删除
void erase(iterator pos){
destory(pos);
}
void erase(iterator b , iterator e){
destory(b, e);
}
//出元素
void pop_back(){
pop();
}
//打印数组
std::ostream& print_vec(std::ostream&);
//判断空
bool empty() const{return !data;}
//添加元素:
void insert(iterator it,const T& t){
std::ptrdiff_t dis = it - data;
if(avail == limit) grow();
add(dis, t);
}
template <class In>
void insert(iterator out, In b, In e){
std::ptrdiff_t start = out - data;//插入位置
std::ptrdiff_t dis = e - b;
if(dis > 0){//插入区间合法
const size_type new_length = size() + size_type (dis);
size_type container = limit - data;
while(new_length >= container){
grow();
container = limit - data;
}
add(start, b, e);
}
}
template <class In>
void assign(In, In);
//容器有元素的长度
size_type size() const { return avail - data;}
//返回迭代器类型
iterator begin(){ return data;}//读写
const_iterator begin() const { return data;}//只读
iterator end() { return avail;}
const_iterator end() const { return avail;}
private:
iterator data;//指针指向Vec的第一个元素
iterator avail;//指针指向构造元素后面的一个元素
iterator limit;//指针指向最后一个可获得元素后面的一个元素
//内存分配工具
std::allocator<T> alloc;//控制内存块分配的对象
//为底层的数组分配空间并对它进行初始化
void create();
void create(size_type, const T&);
void create(const_iterator, const_iterator);
template <class In> void create(In, In);
//删除数组中的元素并释放其占有的内存
void uncreate();
//支持push_back函数
void grow();
void unchecked_append(const T&);
//支持clear函数
void destory();
//支持erase函数
iterator destory(iterator);
iterator destory(iterator, iterator);
//支持pop_back
void pop();
//支持insert
void add(std::ptrdiff_t,const T&);
void add(std::ptrdiff_t, const_iterator, const_iterator);
};
//赋值运算符
template<class T> Vec<T>& Vec<T>::operator=(const Vec& rhs){
if(&rhs != this){//this:指向操作数对象的指针
//删除运算符左侧的数组
uncreate();
//从右侧复制元素到左侧
create(rhs.begin(), rhs.end());
}
//返回一个指向表达式做操作数对象的一个引用调用
//该对象生存周期大于赋值操作
//保证了函数返回的时候不被删除
return *this;//返回指向的对象
}
//默认构造
template<class T> void Vec<T>::create() {
data = avail = limit = 0;
}
//带一个参数大小和给初值
template<class 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<class T> void Vec<T>::create(const_iterator i, const_iterator j){
data = alloc.allocate(j - i);
limit = avail = std::uninitialized_copy(i, j, data);
}
template <class T>
template <class In>
void Vec<T>::create(In b, In e) {
data = alloc.allocate(e - b);
limit = avail = std::uninitialized_copy(b, e, data);
}
//删除对象,释放占用的内存
template<class T> void Vec<T>::uncreate(){
//alloc.deallocate函数需要一个非零指针作为参数
//既是它不准备释放任何内存
if(data){
//以相反顺序构造函数生成的元素
iterator it = avail;
while(it != data){//删除对象
alloc.destroy(--it);
}
//返回占用的所有内存空间
alloc.deallocate(data, limit - data);//删除未初始化内存
}
//重置指针以表明Vec类型对象为空
data = limit = avail = 0;
}
//push_back函数的成员函数
template<class T> void Vec<T>::grow(){
size_type new_size;
//扩展对象大小时,为对象分配实际使用的两倍大小的内存空间
//Vec为空的时候,选择一个元素进行分配
new_size = std::max(2 * (limit - data), std::ptrdiff_t(1));
//分配新的内存空间并将已存在的对象元素内容复制搭配新内存中
iterator new_data = alloc.allocate(new_size);
iterator new_avail = std::uninitialized_copy(data, avail, new_data);
//返回原来的内存空间
uncreate();
//重置指针,使其指向新分配的内存空间
data = new_data;
avail = new_avail;
limit = data + new_size;
}
//假设avail指向一片新分配的但尚未初始化的内存空间
template<class T> void Vec<T>::unchecked_append(const T& val){
alloc.construct(avail++, val);
}
template <class T>
void Vec<T>::destory() {
iterator it = avail;
while(it != data){
alloc.destroy(--it);
}
avail = data;
}
template <class T>
typename Vec<T>::iterator Vec<T>::destory(Vec::iterator pos) {
if(data && pos < avail && pos >= data){
alloc.destroy(pos);
iterator it = pos +1;
while(it != avail){
alloc.construct(pos++, *it++);
alloc.destroy(pos);
}
avail = pos;
}
return avail;
}
template <class T>
typename Vec<T>::iterator Vec<T>::destory(Vec<T>::iterator b, Vec<T>::iterator e) {
if(data && b < e && e < avail && b >= data){
iterator it = b;
while(it != e){
alloc.destroy(it++);
}
while(e != avail){
alloc.construct(b++, *e);
alloc.destroy(e++);
}
avail = b;
}
return avail;
}
template<class T>
std::ostream& Vec<T>::print_vec(std::ostream &os) {
if(avail - data > 0){
const_iterator iter = data;
os << *iter++;
while(iter != avail){
os << " " << *iter++;
}
os << std::endl;
}
return os;
}
template <class T>
void Vec<T>::pop() {
if(data){
alloc.destroy(--avail);
}
}
template <class T>
void Vec<T>::add(std::ptrdiff_t dis, const T& val){
if(dis < avail - data){
iterator e = avail;
iterator b = avail -1;
while(e != data + dis){
alloc.construct(e--, *b--);
alloc.destroy(b);
}
alloc.construct(data + dis, val);
++avail;
}
}
template <class T>
void Vec<T>::add(std::ptrdiff_t start,Vec<T>::const_iterator b, Vec<T>::const_iterator e) {
iterator pos = data + size_type(start);
if(size_type(start)> size()) throw std::domain_error("");
Vec<T>temp;
iterator insert_pos = pos;
for ( ; insert_pos != avail; ++insert_pos) {//原插入位置的后面部分暂存
temp.push_back(*insert_pos);
}
for (const_iterator it = b; it != e; ++it) {//覆盖插入元素
*insert_pos++ = *it;
}
for (const_iterator it = temp.begin(); it != temp.end(); ++it) {//把愿插入位置后面的部分重新插入
*insert_pos++ = *it;
}
//更新avail
avail = insert_pos;
// std::copy(pos, avail, temp);
// std::copy(temp.begin(),temp.end(),std::copy(b, e, pos));
}
template <class T>
template <class In>
void Vec<T>::assign(In b, In e) {
uncreate();
create(b, e);
}
#endif
测试程序:
#include <iostream>
#include "Vec.h"
#include <vector>
using std::vector;
using std::cin; using std::cout;
using std::endl;
int main(int argc, char const *argv[]){
//测构造函数
Vec<int> v;//默认构造
Vec<int> v2(4);//带一个大小参数
Vec<int> v3(4,6);//带一个大小参数和初值
//cout << v3.size() << endl;
// for (Vec<int>::size_type j = 0; j != v3.size(); ++j) {//测索引
// cout << v3[j] <<" ";
// }
//测复制构造函数
// v.push_back(3);//测push_back
// v.push_back(1);
// v.push_back(10);
// v.push_back(7);
//隐式复制操作
// int d = max_Vec(v);//将vi作为参数传递给max_Vac函数
// cout << d;
// Vec<int> v4 = sort_Vec(v);//将sort_Vec函数的返回值赋给v4 //初始化
// for (Vec<int>::size_type j = 0; j != v4.size(); ++j) {
// cout << v4[j] <<" ";
// }
//显示复制
// Vec<int> v5 = v3;
// for (Vec<int>::size_type j = 0; j != v5.size(); ++j) {
// cout << v5[j] <<" ";
// }
//赋值
// Vec<int> v4 = v;//初始化
// v3 = v4;//赋值操作,已存在的值擦去,以新值代替
// for (Vec<int>::size_type j = 0; j != v3.size(); ++j) {
// cout << v3[j] <<" ";
// }
//测删除、清空
// v.push_back(1);//测push_back
// v.push_back(2);
// v.push_back(3);
// v.push_back(4);
v.erase(v.begin());
// v.erase(v.begin(), v.begin()+3);
v.pop_back();
v.clear();
// v.print_vec(cout);
// for (Vec<int>::const_iterator it = v.begin(); it != v.end(); ++it) {
// cout << *it <<" ";
// }
//测empty
// if(!v.empty()){
// cout << "Yes"<<endl;
// }else{
// cout << "No"<<endl;
// }
// if(!v3.empty()){
// cout << "Yes"<<endl;
// }else{
// cout << "No" <<endl;
// }
//测试insert单个
// v3.insert(v3.begin()+ 5, 10);
// v3.print_vec(cout);
//测insert区间
v3.insert(v3.begin()+ 3, 10);
Vec<int> v4(100,66);
v3.insert(v3.begin()+1, v4.begin(), v4.end());
v3.print_vec(cout);
return 0;
}