C++实现Vector类,使用Placement new 和 move 语义

    在计蒜客上学习 轮子哥的 面向对象的程序设计C++ 第二章,最后的一道题是 使用move实现Vector类。

    在这一章中学习了 forward(完美转发),move(移动语义),最后一小节学习了Placment new的用法。最终按照题目要求完成了Vector类的实现,有些心得体会,自此记录于此。

    前言:如果是计蒜客的同学上网搜寻资料打开这篇文章,请关闭该主题,自行完成题目,遵守《学术准则》。



分割线

==========================================================================

什么是placement new?

所谓placement new是在用户指定的内存位置构建新的对象,这个构建过程不需要额外分配内存,只需要调用对象的构造函数即可。

即是把普通new分为两步,举例来说:

    1)T* it = static_cast<T*>( malloc( sizeof(T) ) ); 

    2)new( it )T( );

    分配sizeof(T)大小内存空间,地址赋给 it ,在 it 指向的空间构造 T。

    更详细介绍在这篇文章


回到主题,题目要求实现Vector类,在测试中,用它来填充元素 Element ,它在调用不同的构造函数的时候会输出不同文字信息。所以需要正确使用std::move,调用正确的构造函数,才能得到正确的结果。

纠正点:

    1)析构函数中对指针判断是否为nullptr是不必要的,因为delete一个空指针不会发生错误。

    2)对于右值引用构造函数,需要把参数的指针设置为nullptr,其它譬如count等对象的私有属性均置为0,即将其初始化。因为无法确保不经意间再次调用被当做右值引用的参数(当这个参数还未被析构的时候),这样可以确保不出现其它问题。

    3)如有错误,欢迎提出指正。一个小白程序员的学习旅程。

代码如下:

#include <iostream>
#include <cstdlib>
#include <cstring>
using namespace std;
class Element {
private:
	int number;
public:
	Element() :number(0) {
		cout << "ctor" << endl;
	}
	Element(int num) :number(num) {
		cout << "ctor" << endl;
	}
	Element(const Element& e) :number(e.number) {    //复制构造函数
		cout << "copy ctor" << endl;
	}
	Element(Element&& e) :number(e.number) {    //右值引用构造函数
		cout << "right value ctor" << endl;
	}
	~Element() {
		cout << "dtor" << endl;
	}
	void operator=(const Element& item) {
		number = item.number;
	}
	bool operator==(const Element& item) {
		return (number == item.number);
	}
	void operator()() {
		cout << number;
	}
	int GetNumber() {
		return number;
	}
};
template<typename T>
class Vector {
private:
	T * items;
	int count;
public:
	Vector() :count{ 0 }, items{ nullptr } {

	}

	Vector(const Vector& vector) :count{ vector.count } {
		items = static_cast<T*>(malloc(sizeof(T) * count));
		memcpy(items, vector.items, sizeof(T) * count);
	}

	Vector(Vector&& vector) :count{ vector.count }, items{ vector.items } {
		//TODO
		vector.count = 0;
		vector.items = nullptr;
	}

	~Vector() {    //调用Clear()函数,由于Clear()并不对items析构,且将count置为0,所以不会引发重复析构错误
		//TODO
		Clear();
		delete items;
	}
	T& operator[](int index) {
		if (index < 0 || index >= count) {
			cout << "invalid index" << endl;
			return items[0];
		}
		return items[index];
	}
	int returnCount() {
		return count;
	}  
	void Clear(){	//需要调用各个元素的析构函数,但是并不对items析构
		for (int i = 0; i < count; i++)
		{
			items[i].~T();
		}
		items = nullptr;
		count = 0;
	}

	void Add(const T& item) {    //类似STL的Vector.push_back(),但是STL中Vector有预存的内存(capacity),不需要每次添加都重新分配
		                     //详细可以查看 Vector 的 push_back 实现原理
		T *newitems = static_cast<T*>(malloc(sizeof(T)*(count + 1)));    
		for (int i = 0; i < count; i++)
		{
			new(&newitems[i])T(move(items[i]));    //在新申请的内存构造对象,使用move可以调用对象的右值引用构造函数,提高效率
		}
		new(&newitems[count])T(move(item));
		for (int i = 0; i < count; i++)
		{
			items[i].~T();    //释放原来的对象
		}
		items = newitems;    //保存重新申请的内存地址
		count++;
	}
	bool Insert(const T& item, int index) {
		//插入元素,操作同Add原理
		if (index < 0 || index >count)    //判断参数合法性
		{
			return false;
		}
		T *newitems = static_cast<T*>(malloc(sizeof(T)*(count + 1)));
		for (int i = 0; i < index; i++)
		{
			new(&newitems[i])T(move(items[i]));
		}
		new(&newitems[index])T(move(item));
		for (int i = index; i < count; i++)
		{
			new(&newitems[i + 1])T(move(items[i]));
		}
		for (int i = 0; i < count; i++)
		{
			items[i].~T();
		}
		items = newitems;
		count++;
		return true;
	}
	bool Remove(int index) {
		//移除元素,操作同Add原理
		if (index < 0 || index >=count)
		{
			return false;
		}
		T *newitems = static_cast<T*>(malloc(sizeof(T)*(count - 1)));
		for (int i = 0; i < index; i++)
		{
			new(&newitems[i])T(move(items[i]));
		}
		for (int i = index; i < count-1; i++)
		{
			new(&newitems[i])T(move(items[i+1]));
		}
		for (int i = 0; i < count; i++)
		{
			items[i].~T();
		}
		items = newitems;
		count--;
		return true;
	}
	int Contains(const T& item) {    //寻找元素,匹配返回索引,不匹配返回-1
		for (int i = 0; i < count; i++)
		{
			if (items[i] == item)
			{
				return i;
			}
		}
		return -1;
		
	}
};
template<typename T>
void PrintVector(Vector<T>& v) {    //遍历Vector输出
	int count = v.returnCount();
	for (int i = 0; i < count; i++)
	{
		v[i]();
		cout << " ";
	}
	cout << endl;
}
int main() {    //测试用例
	Vector<Element>v;
	for (int i = 0; i < 4; i++) {
		Element e(i);
		v.Add(e);
	}

	PrintVector(v);
	Element e2(4);

	if (!v.Insert(e2, 10))
	{
		v.Insert(e2, 2);
	}
	PrintVector(v);

	if (!v.Remove(10))
	{
		v.Remove(2);
	}
	PrintVector(v);

	Element e3(1), e4(10);
	cout << v.Contains(e3) << endl;
	cout << v.Contains(e4) << endl;

	Vector<Element>v2(v);

	Vector<Element>v3(move(v2));
	PrintVector(v3);
	v2.Add(e3);
	
	PrintVector(v2);

	return 0;
}
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页