vector增容过程中的潜在问题

62 阅读3分钟

vector增容存在的问题,下面笔者给出目前遇到的两个,后续遇到则继续补充

1 浅拷贝问题

  1. 当开辟新空间时候,若就空间内存在指针,比如存放string,采用 memcpy() 则会导致浅拷贝问题

image-20231224220759028.png

【解决】通过赋值操作,调用string的 operator= 操作

 void reserve(size_t n) {
     if (n > capacity()) {
         size_t num = size();
         // 开辟空间,转移数据
         T* tmp = new T[n];
         // 浅拷贝问题
         //if (_start) memcpy(tmp, _start, sizeof(T) * num);
         if (_start) {
             for (size_t i = 0; i < num; ++i) {
                 tmp[i] = _start[i];    // 本质调用string类的operator=深拷贝, 即 string s1 = s2;
             }
             delete[] _start;
         }

         _start = tmp;
         _finish = tmp + num;
         _end_of_storage = tmp + n;
     }   
 }

2 迭代器失效

1.开辟新空间后,自身的迭代器失效

 // 重新设置自身迭代器
 iterator = vector.begin();

2 开辟新空间后,函数传参的迭代器失效

 // 重新设置
 iterator insert(iterator pos, const T& x) {
     assert(pos <= _finish);
     if (_finish == _end_of_storage) {
         size_t num = size();
         size_t newcap = (capacity() == 0) ? 2 : capacity() * 2;
         reserve(newcap);
         // 更新pos位置
         pos = _start + num;
     }
     // 移动数据 (有数据的时候)
     if (_start != _finish) {
         iterator cur = _finish - 1;
         while (cur >= pos) {
             *(cur + 1) = *cur;
             --cur;
         }
     }
     // 插入 (无数据则直接插入)
     *pos = x;
     ++_finish;
     return pos;
 }

3 最后给出vector基本的成员函数

#pragma once
#include <iostream>
#include <assert.h>
using namespace std;
namespace yu 
{
	template <class T>
	class myvector {
	public:
		// ****************************** 迭代器
		typedef T* iterator;
		typedef const T* const_iterator;

		iterator begin() const{
			return _start;
		}
		const_iterator cbegin() const {
			return _start;
		}
		iterator end() const{
			return _finish;
		}
		const_iterator cend() const {
			return _finish;
		}

		// ******************************

		// ****************************** 构造函数
		// 1 默认构造
		myvector() : _start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
		{

		}
		// 2 有参构造
		vector(size_t n, const T& value = T())
			: _start(nullptr)
			, _finish(nullptr)
			, _endOfStorage(nullptr)
		{
			reserve(n);
			while (n--)
			{
				push_back(value);
			}
		}

		/*
		* 理论上将,提供了vector(size_t n, const T& value = T())之后
		* vector(int n, const T& value = T())就不需要提供了,但是对于:
		* vector<int> v(10, 5);
		* 编译器在编译时,认为T已经被实例化为int,而10和5编译器会默认其为int类型
		* 就不会走vector(size_t n, const T& value = T())这个构造方法,
		* 最终选择的是:vector(InputIterator first, InputIterator last)
		* 因为编译器觉得区间构造两个参数类型一致,因此编译器就会将InputIterator实例化为int
		* 但是10和5根本不是一个区间,编译时就报错了
		* 故需要增加该构造方法
		*/
		vector(int n, const T& value = T())
			: _start(new T[n])
			, _finish(_start + n)
			, _endOfStorage(_finish)
		{
			for (int i = 0; i < n; ++i)
			{
				_start[i] = value;
			}
		}

		// 若使用iterator做迭代器,会导致初始化的迭代器区间[first,last)只能是vector的迭代器
		// 重新声明迭代器,迭代器区间[first,last)可以是任意容器的迭代器
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}
		// 3 拷贝构造  法1
#if 0
		myvector(const myvector<T>& v): _start(nullptr), _finish(nullptr), _end_of_storage(nullptr) 
		{
			_start = new T[v.capacity()];
			_finish = _start;
			_end_of_storage = _start + v.capacity();
			for (size_t i = 0; i < v.size(); ++i) {
				*_finish = v[i];
				++_finish;
			}
		}
#endif
#if 1
		// 法2
		myvector(const myvector<T>& v) : _start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
		{
			reserve(v.capacity());
			// 范围for调用的是const成员函数 begin()
			for (const auto& e : v) {
				push_back(e);
			}
			
		}
#endif
		// 4 赋值运算符重载 法1
		// v1 = v2
#if 0
		myvector<T>& operator=(const myvector<T>& v) {
			myvector<T> tmp(v);
			_start = nullptr;
			_finish = nullptr;
			_end_of_storage= nullptr;
			swap(_start, tmp._start);
			swap(_finish, tmp._finish);
			swap(_end_of_storage, tmp._end_of_storage);

			return *this;

		}
#elif 1
		// 法2 通过传值拷贝 现代写法
		void swap( myvector<T> v) {
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_end_of_storage, v._end_of_storage);
		}
		myvector<T>& operator=( myvector<T> v) {
			swap(v);
			return *this;
			
		}
#endif
		// 5 析构函数
		~myvector() {
			delete[] _start;
			_start = _finish = _endOfStorage = nullptr;
		}
   }