顺序容器和空间配置器(C++)

1.容器的基本概念

(1)容器是一种容纳特定类型对象的集合。C++的容器可以分为两类:顺序容器和关联容器。顺序容器的元素排列和元素值大小无关,而是由元素添加到容器中的次序决定的。
(2)顺序容器中,元素的排放顺序是与其加入容器时的位置相对应的,这种顺序不依赖于元素的值。
(3)关联容器中,元素的位置由相关联的关键字值决定的。也就是所关联容器中元素的位置与关键字关联。
(4)标准库定义了三种顺序容器的类型:vector、list和deque。此外,标准库还提供了三种容器适配器:stack、queue和prioroty_queue类型。适配器是根据原始的容器类型所提供的操作,通过定义新的操作接口,来适应基础的容器类型。见下:

顺序容器
vector                    支持快速随机访问
list                      支持快速插入/删除
deque                     双端队列

顺序容器适配器
stack                     后进先出(LIFO)栈
queue                     先进先出(FIFO)队列
priority_queue            有优先级管理的队列

2.顺序容器的种类

------------------------------> 顺序容器都提供了快速顺序访问元素的能力 <---------------------------------------
(1)vector:可变大小数组,支持快速随机访问,可快速增长,但是插入或删除元素可能很慢。
(2)deque:双端队列,支持快速随机访问,在首尾插入、删除速度很快。
(3)list:双向链表,双向顺序访问/遍历(链表不支持元素的随机访问),list在任何位置的插入和删除速度都很快,很方便。
(4)forward_list:单向链表,单向顺序访问。
(5)array:固定大小数组,支持随机快速访问,不能删除或填加元素。
(6)string:字符串容器。

3.关于顺序容器的使用总结

(1)除了array固定大小,其他顺序容器都提供高效、灵活的内存管理,可以添加、删除、扩张和收缩容器。
(2)string 和 vector 等容器将元素保存在连续的内存空间中,即其元素的内存是连续存储的,可以支持下标操作。
(3)通常使用 vector。
(4)如果在读取时需要插入元素,但是随后需要使用随机访问元素,根据情况可以选择sort重排vector,更通用情况是输入阶段使用list,输入完成后,将list中的内容放入到一个vector中。

4.空间配置器

首先看不带空间配置器的vector容器

template<typename T>
class Vector
{
public:
	// 按指定size进行构造,size个空间,没有元素
	Vector(int size = 0):mCur(0),mSize(size)
	{
		if (size == 0)
		{
			mpVec = nullptr;
		}
		else
		{
			mpVec = new T[mSize];
		}
	}
	// 按指定size进行构造,添加size个元素,元素值是val
	Vector(int size, const T &val = T()):mCur(size)
	{
		mpVec = new T[size];
		mSize = size;
		for (int i = 0; i < size; ++i)
		{
			mpVec[i] = val;
			
		}
	}
	// 按[first, last)区间的元素来构造Vector
	Vector(T *first, T *last)
	{
		int size = last - first;
		mSize = size;
		mpVec = new T[mSize];
		for (mCur = 0; mCur < size; first++)
		{
			mpVec[mCur++] = *first;
		}
	}
	// 从末尾添加元素
	void push_back(const T &val)
	{
		if (full())
		{
			resize();
		}
		mpVec[mCur++] = val;
	}
	// 从末尾删除元素
	void pop_back()
	{
		if (empty())
		{
			return;
		}
		--mCur;
	}
	bool full()const
	{
		return mCur == mSize;
	}
	bool empty()const
	{
		return mSize == 0;
	}
	// 返回容器元素的个数
	int size()const
	{
		return mCur;
	}
	// Vector的迭代器
	class iterator
	{
	public:
		iterator(T *argv = nullptr):_mpVec(argv) {}
		void operator++()
		{
			_mpVec++;
		}
		bool operator!=(const iterator &rhs)
		{
			return _mpVec != rhs._mpVec;
		}
		T& operator*()
		{
			return *_mpVec;
		}
	private:
		T *_mpVec;
	};
	iterator begin()
	{
		return iterator(mpVec);
	}
	iterator end()
	{
		return iterator(mpVec + size());
	}
	
private:
	T *mpVec;
	int mCur;
	int mSize;
	// 容器内存2倍扩容
	void resize()
	{
		if (empty())
		{
			mpVec = new T[1];
			mSize = 1;
			mCur = 0;
		}
		else
		{
			T *tmp = new T[mSize * 2];
			for (int i = 0; i < mSize; i++)
			{
				tmp[i] = mpVec[i];
			}
			delete []mpVec;
			mpVec = tmp;
			mSize *= 2;
		}
	}
};
int main()
{
	Vector<int> vec1; // 底层不开辟空间
	//vec1.push_back(10); // 0 - 1 - 2 - 4 - 8 - 16 - 32 - 64 - 128
	//vec1.push_back(20);
	for (int i = 0; i < 20; ++i)
	{
		vec1.push_back(rand() % 100 + 1);
	}
	cout << vec1.size() << endl;
	
	// 用通用的迭代器遍历方式,遍历vec1,并打印容器中所有的元素值
	Vector<int>::iterator it = vec1.begin();
	for (; it != vec1.end() ; ++it)
	{
		cout << *it << " ";
	}
	cout << endl;

	Vector<int> vec2(10, 20);

	Vector<int>::iterator it1 = vec2.begin();
	for (; it1 != vec2.end() ; ++it1)
	{
		cout << *it1 << " ";
	}
	cout << endl;

	int arr[] = { 12,4,56,7,89 };
	Vector<int> vec3(arr, arr + sizeof(arr) / sizeof(arr[0]));

	Vector<int>::iterator it2 = vec3.begin();
	for (; it2 != vec3.end() ; ++it2)
	{
		cout << *it2 << " ";
	}
	cout << endl;
	return 0;
}

容器的空间配置器是什么?

(1)一般来说,C++内存配置和释放操作是new和delete。
(2)对于new,我们都是先配置内存,然后调用对应的构造函数;而delete则是先调用对应的析构函数,然后释放内存。
(3)有时候我们只想要开辟一定大小的内存,并不想去构造对象,删除也只想对对象进行析构,而不想释放内存,方便继续使用该内存。于是容器的空间配置器产生(使用容器的空间配置器的目的就是把对象的内存开辟和对象的构造分开;把对象的析构和内存释放分开)。

// 实现容器的空间配置器
template<typename T>
class Allocator
{
public:
	T* allocate(size_t size) // 开辟内存
	{
		return (T*)malloc(size);
	}
	void deallocate(void *ptr) // 释放内存
	{
		free(ptr);
	}
	void construct(T *ptr, const T &val) // 构造
	{
		new (ptr) T(val);
	}
	void destroy(T *ptr) // 析构
	{
		ptr->~T();
	}
};

实现空间配置器的Vector

// 实现容器的空间配置器
template<typename T>
class Allocator
{
public:
	T* allocate(size_t size) // 开辟内存
	{
		return (T*)malloc(size);
	}
	void deallocate(void *ptr) // 释放内存
	{
		free(ptr);
	}
	void construct(T *ptr, const T &val) // 构造
	{
		new (ptr) T(val);
	}
	void destroy(T *ptr) // 析构
	{
		ptr->~T();
	}
};

template<typename T, 
		typename allocator = Allocator<T>>
class Vector
{
public:
	// 按指定size进行构造,size个空间,没有元素
	Vector(int size = 0)
		:mCur(0), mSize(size)
	{
		if (size == 0)
		{
			mpVec = nullptr;
		}	
		else
		{
			//mpVec = new T[mSize];
			mpVec = mAllocator.allocate(mSize * sizeof(T));
		}
	}
	
	// 按指定size进行构造,添加size个元素,元素值是val
	Vector(int size, const T &val)
		:mCur(size), mSize(size)
	{
		mpVec = new T[mSize];
		for (int i = 0; i < mSize; ++i)
		{
			mpVec[i] = val;
		}
	}
	
	// 按[first, last)区间的元素来构造Vector
	Vector(T *first, T *last)
	{
		int size = last - first;
		mSize = size;
		mpVec = new T[mSize];
		for (mCur=0; first < last; ++first)
		{
			mpVec[mCur++] = *first;
		}
	}
	
	~Vector() 
	{ 
		//delete[]mpVec; 
		// 析构有效的对象
		for (int i = 0; i < mCur; ++i)
		{
			mAllocator.destroy(mpVec+i);
		}
		// 释放内存
		mAllocator.deallocate(mpVec);
	}
	
	// 从末尾添加元素
	void push_back(const T &val)
	{
		if (full())
			resize();
		//mpVec[mCur++] = val;
		mAllocator.construct(mpVec+mCur, val);
		mCur++;
	}
	
	// 从末尾删除元素
	void pop_back()
	{
		if (empty())
			return;
		--mCur;
		mAllocator.destroy(mpVec+mCur);
	}
	
	bool full()const { return mCur == mSize; }
	bool empty()const { return mCur == 0; }
	
	// 返回容器元素的个数
	int size()const { return mCur; }
	
	// Vector的迭代器
	class iterator
	{
	public:
		iterator(T *p = nullptr) :_ptr(p) {}
		bool operator!=(const iterator &it)
		{
			return _ptr != it._ptr;
		}
		void operator++() { _ptr++; }
		T& operator*() { return *_ptr; }
	private:
		T *_ptr;
	};
	iterator begin() { return iterator(mpVec); }
	iterator end() { return iterator(mpVec + size()); }
	
private:
	T *mpVec;
	int mCur;
	int mSize;
	allocator mAllocator;  // 存储容器的空间配置器

	// 容器内存2倍扩容
	void resize()
	{
		if (0 == mSize)
		{
			mCur = 0;
			mSize = 1;
			mpVec = mAllocator.allocate(mSize * sizeof(T));
		}
		else
		{
			T *ptmp = mAllocator.allocate(2 * mSize * sizeof(T));
			for (int i = 0; i < mSize; ++i)
			{
				mAllocator.construct(ptmp + i, mpVec[i]);	
			}
			for (int i = 0; i < mSize; ++i)
			{
				mAllocator.destroy(mpVec + i);	
			}
			mAllocator.destroy(mpVec);
			mpVec = ptmp;
			mSize *= 2;
		}
	}
};

class A
{
public:
	A() :p(new int[2]) { cout << "A()" << endl; }
	A(const A &src) { cout << "A(const A&)" << endl; }
	~A() { cout << "~A()" << endl; }
private:
	int *p;
};
int main()
{
	A a1, a2, a3;
	cout << "------------" << endl;

	// 这里只需要空间,不需要构造对象  malloc
	Vector<A> vec(100);
	vec.push_back(a1);
	vec.push_back(a2);
	vec.pop_back();
	vec.push_back(a3);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值