STL_vector关于迭代器和深拷贝的学习

本文详细分析了C++中vector迭代器在VS和g++的不同实现,讨论了`reserve`、`erase`和`insert`操作可能导致的迭代器失效问题,以及深拷贝和浅拷贝的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

迭代器

1.对比vs和g++下的迭代器

1.reserve类

2.erase

2.在实现insert和erase函数,如何解决迭代器失效问题

深拷贝

vector实现源码


迭代器

1.对比vs和g++下的迭代器

vector其实是表示可变大小数组的序列容器。

是在连续空间下的,可以随机访问。满足数组的特性。

在vs和g++的vector迭代器其实本质都是指针,但是vs对指针进行了封装。

Linux下g++编译器,vector的迭代器就是原生态指针T* 。(T是模板)

迭代器失效,实际就是迭代器底层对应指针所指向的 空间被销毁了而使用一块已经被释放的空间,造成的后果是程序崩溃(即如果继续使用已经失效的迭代器, 程序可能会崩溃)。

可能导致迭代器失效的情况有很多:

1.reserve类

void test()
{
    kele::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);

    auto pos = find(v.begin(), v.end(), 2);
    v.reserve(20);
}

在reserve过程中原先pos指向的位置已经被销毁了属于野指针,所以迭代器过期;

只要经历过扩容的,都有可能会使迭代器失效,比如push_back,insert,resize等等

2.erase

有两种情况

第一种

对于这种情况,pos位置不变,但是代表的是新的值,迭代器没有失效

第二种

如果删除的是最后一个值,那么pos迭代器如果接下来继续使用就属于非法的了,虽然可能这种错误在g++中不会报错,但显然这是错误的。

erase在vs下迭代器使用过后,就认定为失效了。在g++下没有这么严格,因为,vs的迭代器是类而g++的只是一个指针,没有检查。

同理pop_back也有可能影响迭代器

2.在实现insert和erase函数,如何解决迭代器失效问题

		iterator insert(iterator pos, const T& val)
		{
			assert(pos >= _start);//检查pos
			assert(pos <= _finish);

			if (_finish == _end_of_storage)//判断扩容
			{
				size_t len = pos - _start;
				reserve(_start == nullptr ? 4 : capacity() * 2);
				pos = _start + len;
			}

			iterator end = _finish - 1;//为插入挪数据
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}
			*pos = val;//拷贝插入
			++_finish;

			return pos;
		}
        
        iterator erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos < _finish);

			iterator begin = pos + 1;
			while (begin <= _finish)
			{
				*(begin - 1) = *begin;
				++begin;
			}
			--_finish;

			return pos;
		}
if (_finish == _end_of_storage)//判断扩容
{
        size_t len = pos - _start;
        reserve(_start == nullptr ? 4 : capacity() * 2);
        pos = _start + len;
}

在这一段如果在reserve前后没有对pos进行处理,程序就会崩溃,原因是迭代器失效,while循环要么死循环,要不进行。

虽然reserve前后pos的地址改变,但是pos距离_start起始位置的距离不变,所以len就是这么来的

但是这并没有完全解决问题,经过insert后pos迭代器还是失效了,因为传参时,是传值的,函数内pos更新了,但是对于外面的没有改变。

那么就有一种思路,为什么不用引用呢?传址不就行了嘛?

iterator begin(){return _start;}
v.insert(v.begin(), 10);

这里v.begin()返回的是_start的临时对象具有常性如果引用就会发生权限放大,报错

所以这里用返回值,每次使用迭代器之前都要更新一下。


深拷贝

namespace kele
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;

	private:
		iterator _start = nullptr;
		iterator _finish = nullptr;
		iterator _end_of_storage = nullptr;
	};
}

浅拷贝在开空间后memcpy

深拷贝在开空间后调用赋值重载,

对应内置类型,memcpy可以完成拷贝任务,但是如果是自定义类就会有问题

例如:vector<string>

在析构时就会发生重复析构,而且在对其中一个对象操作会影响另一个。

所以在写拷贝构造就要注意这个问题

		vector(const vector<T>& v)
		{
			if (v._start)//空对象不处理
			{
				reserve(v.capacity());
				size_t v_size = v.size();
				for (size_t i = 0; i < v_size; ++i)
				{
					_start[i] = v._start[i];调用自定义类型的赋值重载
				}
				_finish = _start + v_size;
			}
		}

除此之外还有一种vector<vector<int>>

同样的道理,但是他的自定义类型就是自己,所以要自己写赋值构造

		vector<T>& operator=(const vector<T>& v)
		{
			if (v._start)//空对象不处理
			{
				delete[] _start;
				reserve(v.capacity());
				size_t v_size = v.size();
				for (size_t i = 0; i < v_size; ++i)
				{
					_start[i] = v._start[i];//调用赋值构造
				}
				_finish = _start + v_size;
				return *this;
			}
		}

那vector<vector<vector<int>>>呢?

其实是一个道理,会一直调用赋值重载,直到是内置类型


		vector(size_t n, const T& val = T())
		{
			reserve(n);
			while (n--)
			{
				push_back(val);
			}
		}

这个构造函数是构造n个元素

T是模板可以是任何类型

T() 匿名对象 只在当前行有效,但是如果是

const T& val = T();

那么匿名对象的生命周期就和val一样,因为val就是匿名对象的名字。

const string& val = string();

可以理解,但这些也可以

const int& val = int();
const double& val1 = double();
const int*& val2 = int*();//不可直接使用,报错

为了满足模板的缺省参数是匿名对象,语言支持内置类型构成匿名对象,指针在模板时才行。


vector实现源码

vector.h

#pragma once

#include <iostream>
#include <assert.h>
using namespace std;

namespace kele
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;

		vector()
		{}

		vector(int n, const T& val = T())//int n 防止报错
		{
			reserve(n);
			while (n--)
			{
				push_back(val);
			}
		}

		template <class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		vector(const vector<T>& v)
		{
			if (v._start)
			{
				reserve(v.capacity());
				size_t v_size = v.size();
				for (size_t i = 0; i < v_size; ++i)
				{
					_start[i] = v._start[i];
				}
				_finish = _start + v_size;
			}
		}

		vector<T>& operator=(const vector<T>& v)
		{
			if (v._start)
			{
				delete[] _start;
				reserve(v.capacity());
				size_t v_size = v.size();
				for (size_t i = 0; i < v_size; ++i)
				{
					_start[i] = v._start[i];
				}
				_finish = _start + v_size;
				return *this;
			}
		}

		~vector()
		{
			delete[] _start;
			_start = _finish = _end_of_storage = nullptr;
		}

		iterator begin(){return _start;}

		iterator end(){return _finish;}

		const_iterator begin() const{return _start;}

		const_iterator end() const{return _finish;}

		size_t size() const
		{
			return _finish - _start;
		}

		size_t capacity() const
		{
			return _end_of_storage - _start;
		}

		void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t sz = size();//
				iterator tmp = new T[n];
				if (_start)
				{
					for (size_t i = 0; i < size(); ++i)
					{
						tmp[i] = _start[i];//深拷贝
					}
					delete[] _start;
				}
				_start = tmp;
				_finish = _start + sz;
				_end_of_storage = _start + n;
			}
		}

		void resize(size_t n, const T& val = T())
		{
			if (n <= size())
			{
				_finish = _start + n;
			}
			else
			{
				if (n > capacity())
				{
					reserve(n);
				}
				//memset(_finish, val, (n - size()) * sizeof(T));
				//_finish = _start + n;错误的,对于自定义类型无法赋值
				while (_finish != _start + n)
				{
					*_finish++ = val;
				}
			}
		}

		void push_back(const T& x)
		{
			if (_finish == _end_of_storage)
			{
				reserve(_start == nullptr ? 4 : capacity() * 2);
			}
			*_finish++ = x;
		}

		void pop_back()
		{
			assert(!empty());

			--_finish;
		}

		iterator insert(iterator pos, const T& val)
		{
			assert(pos >= _start);
			assert(pos <= _finish);

			if (_finish == _end_of_storage)
			{
				size_t len = pos - _start;
				reserve(_start == nullptr ? 4 : capacity() * 2);
				pos = _start + len;
			}

			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}
			*pos = val;
			++_finish;

			return pos;
		}

		iterator erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos < _finish);

			iterator begin = pos + 1;
			while (begin <= _finish)
			{
				*(begin - 1) = *begin;
				++begin;
			}
			--_finish;

			return pos;
		}

		T& operator[](size_t pos)
		{
			assert(pos < size());

			return _start[pos];
		}

		const T& operator[](size_t pos) const
		{
			assert(pos < size());

			return _start[pos];
		}

		bool empty()
		{
			return _start == _finish;
		}

	private:
		iterator _start = nullptr;
		iterator _finish = nullptr;
		iterator _end_of_storage = nullptr;
	};

	class Solution {
	public:
		vector<vector<int>> generate(int numRows) 
		{
			vector<vector<int>> vv;
			vv.resize(numRows, vector<int>());
			for (int i = 0; i < numRows; ++i)
			{
				vv[i].resize(i + 1, 0);
				vv[i][0] = vv[i][i] = 1;
			}

			for (int i = 0; i < numRows; ++i)
			{
				for (int j = 0; j < vv[i].size(); ++j)
				{
					if (vv[i][j] == 0)
					{
						vv[i][j] = vv[i - 1][j - 1] + vv[i - 1][j];
					}
				}
			}

			return vv;
		}
	};

}

test.cpp

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

class Solution1 {
    vector<string> date = { "", "","abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz" };
public:
    void Combinations(const string& digits, int dt, vector<string>& ret, string str)
    {
        if (dt == digits.size())
        {
            ret.push_back(str);
            return;
        }

        int num = digits[dt] - '0';

        for (auto x : date[num])
        {
            Combinations(digits, dt + 1, ret, str + x);// str + x 这里是+ 不是+= 至关重要的
        }
    }

    vector<string> letterCombinations(string digits) {
        vector<string> ret;
        if (digits.empty())
        {
            return ret;
        }
        string str;
        Combinations(digits, 0, ret, str);
        return ret;
    }
};

//int main()
//{
//	vector<int> v;
//	v.push_back(1);
//	v.push_back(1);
//	v.push_back(1);
//	v.push_back(1);
//	vector<int>::iterator it = v.begin();
//	while (it != v.end())
//	{
//		cout << *it << " ";
//		++it;
//	}
//	cout << endl;
//
//
//	return 0;
//}

#include"vector.h"
template<class X>
void Print(const kele::vector<X>& v)
{
    //for (int i = 0; i < v.size(); ++i)
    //{
    //    cout << v[i] << " ";
    //}
    //cout << endl;

    //auto it = v.begin();//?
    ////kele::vector<int>::const_iterator it = v.begin();
    //while (it != v.end())
    //{
    //    cout << *it << " ";
    //    ++it;
    //}
    //cout << endl;
    
    for (auto x : v)
    {
        cout << x << " ";
    }
    cout << endl;
    cout << endl;
}

void test1()
{
    kele::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    v.push_back(4);
    v.push_back(4);
    v.push_back(4);

    Print(v);

    v.pop_back();
    v.pop_back();
    v.pop_back();
    Print(v);

    v.resize(20, 0);
    Print(v);

    auto pos = find(v.begin(), v.end(), 0);
    v.insert(pos, 30);
    Print(v);
    pos = find(v.begin(), v.end(), 0);
    v.erase(pos);
    Print(v);
}

void test3()
{
    kele::vector<int> v(5, 2);

    Print(v);

    //kele::vector<string> v2(10, "kele");
    //Print(v2);
    //auto pos = find(v2.begin(), v2.end(), "kele");
    //v2.insert(pos, "hello");
    //Print(v2);

    kele::vector<int> v3(v.begin(), v.end() - 1);
    Print(v3);

    int arr[] = { 50,2,3,1 };
    kele::vector<int> v4(arr, arr + 4);
    Print(v4);
    sort(v4.begin(), v4.end());
    Print(v4);
}

void test2()
{
    kele::vector<string> v;
    v.resize(10, "nihao");
    Print(v);
    auto pos = find(v.begin(), v.end(), "nihao");
    v.insert(pos, "hello");
    Print(v);
}



void test4()
{
    kele::vector<kele::vector<int>> vv1 ;
    vv1 = kele::Solution().generate(8);//赋值构造
    kele::vector<kele::vector<int>> vv = vv1;//防止编译器优化成直接构造
    for (int i = 0; i < vv.size(); ++i)
    {
        for (int j = 0; j < vv[i].size(); ++j)
        {
            cout << vv[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;
}

void test5()
{
    kele::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);

    auto pos = find(v.begin(), v.end(), 2);
    v.reserve(20);


    v.insert(v.begin(), 10);
}


int main()
{
    test4();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值