顺序容器——vector

一、底层实现

vector就是动态数组.它也是在堆中分配内存,元素连续存放,有保留内存,如果减少大小后,内存也不会释放.如果新值>当前大小时才会再分配内存.

它拥有一段连续的内存空间,并且起始地址不变,因此它能非常好的支持随即存取,即[]操作符,但由于它的内存空间是连续的,所以在中间进行插入和删除会造成内存块的拷贝,另外,当该数组后的内存空间不够时,需要重新申请一块足够大的内存并进行内存的拷贝。这些都大大影响了vector的效率。

对最后元素操作最快(在后面添加删除最快 ), 此时一般不需要移动内存,只有保留内存不够时才需要

对中间和开始处进行添加删除元素操作需要移动内存,如果你的元素是结构或是类,那么移动的同时还会进行构造和析构操作,所以性能不高 (最好将结构或类的指针放入vector中,而不是结构或类本身,这样可以避免移动时的构造与析构)。
访问方面,对任何元素的访问都是O(1),也就是是常数的,所以vector常用来保存需要经常进行随机访问的内容,并且不需要经常对中间元素进行添加删除操作.

二、模拟实现(主要实现了个别接口函数和迭代器)

1.MyVector.h

#ifndef MYVECTOR_H
#define MYVECTOR_H

#include<iostream>
#include"Myiterator.h"
using namespace std;

template<typename T>
class MyVector
{
public:
	typedef Myiterator<T> iterator;

	MyVector()      //构造函数
	{
		_arr = NULL;
		_valSize = 0;
		_len = 0;
	}

	MyVector(const MyVector& src)  //拷贝构造函数 
	{
		if (0 == src._len)
		{
			_arr = NULL;
			_valSize = 0;
			_len = 0;

			return;
		}

		_arr = new T[src._valSize];
		for (int i = 0; i<src._valSize; i++)
		{
			_arr[i] = src._arr[i];
		}
	}

	MyVector& operator=(const MyVector &src)   //=运算符重载 
	{
		/*1.防止自赋值
		2.防止内存泄漏
		3.防止浅拷贝*/
		if (this == src)
		{
			return *this;
		}

		if (NULL != _arr)
		{
			delete[] _arr;
		}

		if (0 == src._valSize)
		{
			_arr = NULL;
			_valSize = 0;
			_arr = 0;

			return;
		}

		_arr = new T[src._valSize];
		for (int i = 0; i<src._valSize; i++)
		{
			_arr[i] = src._arr[i];
		}

		return *this;
	}

	~MyVector()          //析构函数 
	{
		if (NULL != _arr)
		{
			delete[] _arr;
		}
	}

	T& operator[](int pos)   //[]运算符重载 
	{
		return 	_arr[pos];
	}

	void push_back(const T&val)  //添加值为val的元素到当前vector末尾
	{
		if (full())
		{
			dilatation();
		}

		_arr[_valSize++] = val;
	}

	void pop_back()           //删除当前vector最末的一个元素
	{
		if (!empty())
		{
			_valSize--;
		}
	}

	T front()                 //返回当前vector起始元素的引用
	{
		if (!empty())
		{
			return _arr[0];
		}
		throw "vector is empty!";
	}

	T back()                  //返回当前vector最末一个元素的引用
	{
		if (!empty())
		{
			return _arr[_valSize - 1];
		}
		throw "vector is empty!";
	}

	int size()                //返回当前vector所容纳元素的数目
	{
		return _valSize;
	}

	void clear()              //删除当前vector中的所有元素
	{
		_valSize = 0;
	}

	void reserve(int len)   //为当前vector预留至少共容纳size个元素的空间
	{
		if (len < _len)
		{
			return;
		}

		T* p = new T[len];
		for (int i = 0; i<_valSize; i++)
		{
			p[i] = _arr[i];
		}

		if (NULL != _arr)
		{
			delete[] _arr;
		}

		_arr = p;
		_len = len;
	}

	bool empty()      //如果当前vector没有容纳任何元素,则empty()函数返回true,否则返回false
	{
		return _valSize == 0;
	}

	bool full()      //判满 
	{
		return _valSize == _len;
	}

	void show()
	{
		for (int i = 0; i<_valSize; i++)
		{
			cout << _arr[i] << " ";
		}
		cout << endl;
	}

	iterator begin()
	{
		return iterator(this, 0);
	}

	iterator end()
	{
		return iterator(this, _valSize);
	}

private:
	T* _arr;
	int _valSize;   //现在存储位置 
	int _len;       //总大小

	void dilatation()  //扩容函数 
	{
		if (_len == 0 || _len == 1)
		{
			_len++;
		}

		_len = _len + (_len >> 1);  //采用1.5倍扩容方式

		T* p = new T[_len];

		for (int i = 0; i<_valSize; i++)
		{
			p[i] = _arr[i];
		}

		if (NULL != _arr)
		{
			delete[] _arr;
		}
		_arr = p;
	}
};

template<typename T>
T& Myiterator<T>::operator*()
{
	return (*_vec)[_pos];
}

template<typename T>
T& Myiterator<T>::operator->()
{
	return (*_vec)[pos];
}

#endif;

2.Myiterator.h

#ifndef MYITERATOR_H
#define MYITERATOR_H
#include<iostream>
using namespace std;

template<typename T>
class MyVector;

template<typename T>
class Myiterator
{
private:
	MyVector<T>* _vec;
	int _pos;
public:
	Myiterator(MyVector<T>* src, int pos)
	{
		_vec = src;
		_pos = pos;
	}

	Myiterator(const Myiterator*src)
	{
		_vec = src._vec;
		_pos = src._pos;
	}

	Myiterator& operator=(const Myiterator& src)
	{
		_vec = src._vec;
		_pos = src._pos;
	}

	bool operator==(const Myiterator& src)
	{
		return (_vec == src._vec && _pos == src._pos);
	}

	bool operator!=(const Myiterator& src)
	{
		return (_vec != src._vec || _pos != src._pos);
	}

	Myiterator& operator++()   //后置++
	{
		++_pos;
		return *this;
	}

	Myiterator operator++(int)
	{
		return Myiterator(_vec, _pos++);
	}

	Myiterator&  operator--()
	{
		--pos;
		return *this;
	}

	Myiterator& operator--(int)
	{
		return Myiterator(_vec, _pos--);
	}

	T& operator*();
	T& operator->();
};

#endif;

3.main.cpp

#include"MyVector.h"
#include<iostream>

using namespace std;

int main()
{
	MyVector<int> v1;
	v1.reserve(5);

	for (int i = 0; i<10; i++)
	{
		v1.push_back(i);
	}
	v1.show();

	cout <<v1.back() << endl;
	cout << v1.front() << endl;

	v1.pop_back();
	v1.show();

	MyVector<int> ::iterator it = v1.begin();
	for (it; it != v1.end(); it++)
	{
		cout <<*it << "    ";
	}
	cout << endl;

	return 0;
}

三、运行结果

### C++ STL 顺序容器与关联容器的区别及应用场景 #### 区别 顺序容器和关联容器的主要区别在于数据存储方式以及访问效率。 - **数据结构** 顺序容器(如`vector`、`list`、`deque`)中的元素按照插入顺序排列,不保证任何特定的排序。对于`vector`而言,其内部实现为动态数组,意味着所有元素存放在一块连续的内存空间里;而`list`则是由节点组成的双向链表,每个节点包含指向前驱和后继节点的指针;至于`deque`,它是一种双端队列,允许高效的两端插入删除操作[^1]。 - **查找性能** 关联容器(如`map`、`set`及其多版本`multimap`、`multiset`),依据键值自动维护有序状态,默认采用红黑树作为底层数据结构来保障快速检索能力——平均时间复杂度接近O(log n),这使得当需要频繁执行查找任务时表现优异。相比之下,除非事先进行了显式的排序处理,否则遍历整个序列将是唯一可行的方法去定位目标项于顺序容器之中,最坏情况下的时间开销可达线性级别即O(n)[^2]。 - **插入/删除成本** 插入新成员到已存在的集合里面,如果是在随机位置的话,则顺序容器通常要比关联容器付出更高的代价,尤其是针对`vector`这种紧凑型布局的数据集,因为可能涉及到大量的元素迁移工作以便腾出足够的空间容纳后来者。不过一旦确定了固定的位置之后,像`list`这样的链接式设计反而能够提供常数级别的附加或移除耗时。另一方面,由于关联容器总是维持着内在秩序不变,所以无论何时何处新增加或是剔除某个实体都不会影响其余部分之间的相对关系,从而保持稳定的对数值级响应速度。 #### 应用场景 - **顺序容器适用场合** 如果应用程序关注的是高效地追加记录至末端或者是按索引获取项目,并且不需要关心这些条目的先后次序之外的信息,那么选择诸如`vector`之类的顺序容器无疑是明智之举。另外,在某些特殊情形下,比如要实施自定义排序逻辑或者利用算法库里的诸多便利函数时,先填充好一个临时性的列表再另行安排也是常见做法之一[^3]。 - **关联容器适合领域** 对于那些依赖关键字来进行精准匹配并期望获得即时反馈的任务来说,显然应该优先考虑使用映射(`map`)或集合(`set`)形式的关联容器。这类工具非常适合用来构建字典、缓存机制以及其他任何形式的关键驱动型数据库模拟体。而且考虑到内置的支持范围查询特性,即使面对海量异构对象也能轻松应对自如。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值