实现C++容器的空间配置器allocator

        容器配置器allocator主要有四个作用,一个是内存开辟,一个是内存释放,一个是对象构造,一个是对象析构,很明显,这是将delete和new的功能拆分开来了,delete和new都是开辟/释放内存的同时,调用对象的构造/析构函数,但这有时候是不适用的,所以我们要将内存开辟和对象构造分开处理。

        比如下面这个通过类模板实现的Vector容器。

#include<iostream>
using namespace std;
template<typename T> class Vector
{
public:
	Vector(int size = 10) :first(new T[size]),
		last(first),
		end(first + size)
	{}
	~Vector()
	{
		delete[]first;
		first = nullptr;
	}
	Vector(const Vector& othervec)
	{
		int size = othervec.end - othervec.first;
		int last_ = othervec.last - othervec.first;
		first = new T[size];
		for (int i = 0; i < last_; i++)
		{
			first[i] = othervec.first[i];
		}
		last = first + last_;
		end = first + size;
	}
	Vector& operator=(const Vector& othervec)
	{
		if (this == &othervec)return *this;
		delete[]first;
		int size = othervec.end - othervec.first;
		first = new T[size];
		int last_ = othervec.last - othervec.first;
		for (int i = 0; i < last_; i++)
		{
			first[i] = othervec.first[i];
		}
		end = first + size;
		last = first + last_;
		return *this;
	}
	void push_back(T val)
	{
		if (full())expand();
		*last++ = val;
	}
	void pop_back()
	{
		if (empty())return;
		last--;
	}
	bool empty()const
	{
		return first == last;
	}
	bool full()const
	{
		return last == end;
	}
	T back()const
	{
		return *(last - 1);
	}
private:
	T* first;
	T* last;
	T* end;
	void expand()
	{
		int size = end - first;
		int last_ = last - first;
		T* newvec = new T[size * 2];
		size *= 2;
		for (int i = 0; i < last_; i++)
		{
			newvec[i] = first[i];
		}
		delete[]first;
		first = newvec;
		end = first + size;
		last = first + last_;
	}
};
class Test
{
public:
	Test()
	{
		cout << "Test()" << endl;
	}
	~Test()
	{
		cout << "~Test()" << endl;
	}
};
int main()
{
	Test t1, t2, t3;
	Vector<Test>v;
	v.push_back(t1);
	v.push_back(t2);
	v.push_back(t3);
	v.pop_back();
	return 0;
}

运行效果如下:byd直接就构造了13个Test对象出来了。 

 

        首先对于这个容器的构造函数,它使用了new来开辟容器的空间,这显然是不对的,假如我们的size默认是10,那么我们在开辟了10个对象大小内存的同时,还构造了10个对象出来,但实际情况是,我们的容器刚开始定义的时候里面是没有对象只有内存的,但是new不仅开辟了内存还构造了对象 。

        第二个就是这个容器的析构函数,delete[]first是通过delelte来释放容器的空间的,但这样也是不对的,因为这样就代表将整个容器的所有数据单元都按照一个对象的方式来析构了,但实际情况是,我们的容器并不是满的,只有一部分空间里面存有了对象,剩余部分空间并没有使用,也就是说,容器的元素个数要少于总空间大小,我们应该析构容器中有效的元素,然后释放first指向的那块堆内存。

        第三个就是pop_back()函数,这个函数同样需要析构对象,但也只是析构对象而已了,不需要析构对象的同时,释放这个对象的内存,因为这块内存是容器本身的,而不是对象的,这时候就不能使用delete了。

        显然上面三个问题我们都可以使用容器的空间配置器allocator来解决,因为allocator的工作就是将内存操作和对象操作分离开来。

        由于Allocator的函数都是公用的,任何我们自定义的容器都可以使用,所以我们将Allocator声明为结构体。

template<typename T>struct Allocator
{
	T* allocate(size_t size)
	{
		return (T*)malloc(size * sizeof(T));
	}
	void deallocate(T* p)
	{
		free(p);
	}
	void construct(T* p, const T& val)
	{
		new (p)T(val);//定位new,在指定的内存上面构造对象
	}
	void destroy(T* val)
	{
		val->~T();//调用该类型的析构函数
	}
};

         Allocator的construct要做的是在一块已有的内存(容器的已经开辟好的内存)上面构造对象,所以我们不能使用普通new,而要使用定位new,它可以在指定的内存中构造对象。

更正Vector的代码

#include<iostream>
using namespace std;
template<typename T>struct Allocator
{
	T* allocate(size_t size)
	{
		return (T*)malloc(size * sizeof(T));
	}
	void deallocate(T* p)
	{
		free(p);
	}
	void construct(T* p, const T& val)
	{
		new (p)T(val);//定位new,在指定的内存上面构造对象
	}
	void destroy(T* val)
	{
		val->~T();//调用该类型的析构函数
	}
};
template<typename T,typename Alloc=Allocator<T>> class Vector
{
public:
	Vector(int size = 10) :first(myallocator.allocate(size)),
		last(first),
		end(first + size)
	{}
	~Vector()
	{
		//delete[]first;
		for (T* p = first; p != last; p++)
		{
			myallocator.destroy(p);
		}
		myallocator.deallocate(first);
		first = nullptr;
	}
	Vector(const Vector& othervec)
	{
		int size = othervec.end - othervec.first;
		int last_ = othervec.last - othervec.first;
		//first = new T[size];
		first = myallocator.allocate(size);
		for (int i = 0; i < last_; i++)
		{
			//first[i] = othervec.first[i];
			myallocator.construct(first+i, othervec.first[i]);
		}
		last = first + last_;
		end = first + size;
	}
	Vector& operator=(const Vector& othervec)
	{
		if (this == &othervec)return *this;
		//delete[]first;
		for (T* p = first; p != last; p++)
		{
			myallocator.destroy(p);
		}
		myallocator.deallocate(first);
		int size = othervec.end - othervec.first;
		//first = new T[size];
		first = myallocator.allocate(size);
		int last_ = othervec.last - othervec.first;
		for (int i = 0; i < last_; i++)
		{
			//first[i] = othervec.first[i];
			myallocator.construct(first+i, othervec.first[i]);
		}
		end = first + size;
		last = first + last_;
		return *this;
	}
	void push_back(const T &val)
	{
		if (full())expand();
		//*last++ = val;
		myallocator.construct(last, val);
		last++;
	}
	void pop_back()
	{
		if (empty())return;
		last--;
		myallocator.destroy(last);//这里只需要析构对象就可以了,不要释放内存,因为内存是容器的
	}
	bool empty()const
	{
		return first == last;
	}
	bool full()const
	{
		return last == end;
	}
	T back()const
	{
		return *(last-1);
	}
private:
	T* first;
	T* last;
	T* end;
	Alloc myallocator;
	void expand()
	{
		int size = end - first;
		int last_ = last - first;
		//T* newvec = new T[size * 2];
		T* newvec = myallocator.allocate(size * 2);
		size *= 2;
		for (int i = 0; i < last_; i++)
		{
			//newvec[i] = first[i];
			myallocator.construct(newvec+i, first[i]);
		}
		//delete[]first;
		for (T* p = first; p != last; p++)
		{
			myallocator.destroy(p);
		}
		myallocator.deallocate(first);
		first = newvec;
		end = first + size;
		last = first + last_;
	}
};
class Test
{
public:
	Test()
	{
		cout << "Test()" << endl;
}
	~Test()
	{
		cout << "~Test()" << endl;
	}
};
int main()
{
	Test t1, t2, t3;
	Vector<Test>v;
	v.push_back(t1);
	v.push_back(t2);
	v.push_back(t3);
	cout << "-------------------------------" << endl;
	v.pop_back();
	return 0;
}

运行效果如下:这才是我们想要的效果,多少个对象就是多少个对象,不多不少。

第一个~Test()是pop_back()函数的作用,第二、三个~Test()是容器中剩下两个Test的析构函数,其余三个是本来main函数里面的三个Test(t1,t2,t3)的析构函数。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咩咩大主教

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值