【C++】C++手写Vector容器:从底层源码模拟实现

📌 相关专栏

很高兴你点开这篇文章✨

这里会持续更新更多有用的内容,关注我,一起慢慢变好呀

👍 点赞 ⭐ 收藏 💬 评论


文章目录

  • 前言
  • [1. 核心架构:三指针内存管理](#1. 核心架构:三指针内存管理)
  • [2. 迭代器设计](#2. 迭代器设计)
  • [3. 构造与析构](#3. 构造与析构)
    • [3.1 无参构造](#3.1 无参构造)
    • [3.2 析构函数](#3.2 析构函数)
    • [3.3 clear:清空数据](#3.3 clear:清空数据)
  • [4. 容量管理:size、capacity、reserve、resize](#4. 容量管理:size、capacity、reserve、resize)
    • [4.1 reserve:预分配空间(核心实现)](#4.1 reserve:预分配空间(核心实现))
    • [4.2 resize:调整有效元素个数](#4.2 resize:调整有效元素个数)
    • [4.3 operator :下标访问](#4.3 operator[ ]:下标访问)
  • [5. 增删操作:push_back、pop_back](#5. 增删操作:push_back、pop_back)
    • [5.1 push_back:尾插](#5.1 push_back:尾插)
    • [5.2 pop_back:尾删](#5.2 pop_back:尾删)
  • [6. 插入与删除:insert、erase(含迭代器失效)](#6. 插入与删除:insert、erase(含迭代器失效))
    • [6.1 insert:指定位置插入](#6.1 insert:指定位置插入)
    • [6.2 erase:指定位置删除](#6.2 erase:指定位置删除)
    • [6.3 迭代器失效问题及解决方案](#6.3 迭代器失效问题及解决方案)
  • [7. 拷贝控制:拷贝构造、赋值运算符、swap](#7. 拷贝控制:拷贝构造、赋值运算符、swap)
    • [7.1 拷贝构造](#7.1 拷贝构造)
    • [7.2 swap:交换三个指针](#7.2 swap:交换三个指针)
    • [7.3 赋值运算符重载(现代写法)](#7.3 赋值运算符重载(现代写法))
  • [8. 扩展构造接口](#8. 扩展构造接口)
    • [8.1 迭代器区间构造(函数模板)](#8.1 迭代器区间构造(函数模板))
    • [8.2 n 个 val 构造](#8.2 n 个 val 构造)
    • [8.3 全局打印函数(typename 的使用)](#8.3 全局打印函数(typename 的使用))
  • [9. 完整测试用例](#9. 完整测试用例)
    • [test 1 :增删操作与迭代器失效](#test 1 :增删操作与迭代器失效)
    • [test 2 :拷贝构造与赋值](#test 2 :拷贝构造与赋值)
    • [test 2 :迭代器区间构造与 n 个 val 构造](#test 2 :迭代器区间构造与 n 个 val 构造)
  • 总结
  • 本文全部代码
    • [🐾 vector.h](#🐾 vector.h)
    • [🐾 Test.cpp](#🐾 Test.cpp)

前言

std::vector 是 C++ 中最常用的容器之一,它封装了动态数组,能够自动扩容。但你知道它是如何通过三个指针来管理内存的吗?为什么 insert 后迭代器会失效?如何实现一个正确的拷贝构造和赋值运算符?

  • 三指针架构:_start、_finish、_end_of_storage
  • 迭代器本质:对原生指针的封装
  • 迭代器失效:扩容和删除导致的野指针问题
  • 深拷贝:自定义类型的内存管理

🐶 🐾 ✨ 🐾 🐶


1. 核心架构:三指针内存管理

vector 本质是动态管理连续内存的容器,通过三个指针(迭代器) 控制内存块:

  • _start:指向内存块的起始位置(第一个元素的地址)
  • _finish:指向有效元素的末尾(最后一个元素的下一位)
  • _end_of_storage:指向内存块的,末尾(容量的边界)
cpp 复制代码
namespace Myvector
{
    template<class T>
    class vector
    {
    private:
        iterator _start = nullptr;          // 指向内存块起始位置(第一个元素)
        iterator _finish = nullptr;         // 指向有效元素末尾(最后一个元素的下一位)
        iterator _end_of_storage = nullptr; // 指向内存块末尾(容量的边界)
    };
}

三个指针的演示图:

三指针的计算指标:

cpp 复制代码
size_t size() const
{
    return _finish - _start;              // 有效元素个数
}

size_t capacity() const
{
    return _end_of_storage - _start;      // 总容量
}

优点 :无需额外成员变量存储 size 和 capacity,通过指针差值即可计算,简洁高效。

🐶 🐾 ✨ 🐾 🐶


2. 迭代器设计

vector 的迭代器就是对原生指针的类型别名:

cpp 复制代码
typedef T* iterator;           // 普通迭代器,可读写
typedef const T* const_iterator; // 常量迭代器,只读

迭代器接口实现:

cpp 复制代码
iterator begin() { return _start; }
iterator end()   { return _finish; }

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

注意 :由于 vector 内存是连续的,原生指针本身就支持 ++、--、+n、-n、* 等操作,符合迭代器要求,因此可以直接用 T* 作为迭代器。

🐶 🐾 ✨ 🐾 🐶


3. 构造与析构

3.1 无参构造

cpp 复制代码
vector() 
    : _start(nullptr)
    , _finish(nullptr)
    , _end_of_storage(nullptr)
{
}

3.2 析构函数

cpp 复制代码
~vector()
{
    if (_start)           // _start 不为空才需要释放
    {
        delete[] _start;
        _start = _finish = _end_of_storage = nullptr;
    }
}

3.3 clear:清空数据

cpp 复制代码
void clear()
{
    _finish = _start;     // 只清空数据,不改变空间大小
}

🐶 🐾 ✨ 🐾 🐶


4. 容量管理:size、capacity、reserve、resize

4.1 reserve:预分配空间(核心实现)

reserve 是最核心的扩容函数,所以我们需要特别注意深拷贝问题:

cpp 复制代码
void reserve(size_t n)
{
    if (n > capacity())
    {
        size_t old_size = size();           // 1. 保存原大小空间
        T* tmp = new T[n];                  // 2. 开辟新空间

        // 3. 拷贝元素(深拷贝)
        // !! 不能用 memcpy!自定义类型会浅拷贝导致问题
        for (size_t i = 0; i < old_size; i++)
        {
            tmp[i] = _start[i];             // 调用赋值运算符重载(深拷贝)
        }

        delete[] _start;                    // 4. 释放原空间
        _start = tmp;                       // 5. 指向新空间
        _finish = _start + old_size;        // 6. 更新 _finish
        _end_of_storage = _start + n;       // 7. 更新容量边界
    }
}

关键点:

  • 不能使用 memcpy 进行浅拷贝,自定义类型(如 string)会出问题
  • 必须逐元素赋值,调用 T 的赋值运算符实现深拷贝
  • 需要提前保存 old_size,因为 delete[] 后 size() 无法使用

4.2 resize:调整有效元素个数

cpp 复制代码
void resize(size_t n, T val = T())  // T() 是匿名对象,作为缺省值,如果调用时不写第二个参数,就要用T类型的默认构造对象来填充

{
    if (n < size())                   // 情况1:缩小
    {
        _finish = _start + n;
    }
    else                              // 情况2:扩大
    {
        if (n > capacity())
        {
            reserve(n);               // 先扩容
        }
        while (_finish < _start + n)  // 填充 val
        {
            *_finish = val;
            _finish++;
        }
    }
}

关于 = T() 的解读:

  • 当 T 是 int 时,T() 就是 0
  • 当 T 是 string 等自定义类型时,T() 调用其默认构造函数
  • 这是模板编程中获取类型默认值的常用技巧

4.3 operator :下标访问

cpp 复制代码
T& operator[](size_t i)
{
    assert(i < size());
    return _start[i];
}

const T& operator[](size_t i) const
{
    assert(i < size());
    return _start[i];
}

🐶 🐾 ✨ 🐾 🐶


5. 增删操作:push_back、pop_back

5.1 push_back:尾插

cpp 复制代码
void push_back(const T& x)
{
    if (_finish == _end_of_storage)           // 空间不足,需要扩容
    {
        reserve(capacity() == 0 ? 4 : 2 * capacity());  // 扩容策略:空时开4,否则翻倍
    }
    *_finish = x;                             // 赋值
    _finish++;                                // 更新 _finish
}

5.2 pop_back:尾删

cpp 复制代码
bool empty() const
{
    return _start == _finish;
}

void pop_back()
{
    assert(!empty());
    --_finish;                                // 只需移动指针
}

注意 :pop_back 并不释放内存,只是将 _finish 前移。

🐶 🐾 ✨ 🐾 🐶


6. 插入与删除:insert、erase(含迭代器失效)

6.1 insert:指定位置插入

cpp 复制代码
void insert(iterator pos, const T& x)
{
    assert(pos >= _start && pos < _finish);

    if (_finish == _end_of_storage)           // 需要扩容
    {
        size_t len = pos - _start;            // 保存偏移量
        reserve(capacity() == 0 ? 4 : 2 * capacity());
        pos = _start + len;                   // 🔴 关键:修正迭代器
    }

    iterator end = _finish - 1;
    while (end >= pos)                        // 从后往前移动元素
    {
        *(end + 1) = *end;
        end--;
    }
    *pos = x;
    _finish++;
}

6.2 erase:指定位置删除

cpp 复制代码
iterator erase(iterator pos)
{
    assert(pos >= _start && pos < _finish);
    iterator it = pos + 1;
    while (it != end())                       // 从前往后移动元素
    {
        *(it - 1) = *it;
        it++;
    }
    _finish--;
    return pos;                               // 返回被删除位置的下一个迭代器
}

6.3 迭代器失效问题及解决方案

什么是迭代器失效?

  • 当 vector 扩容时,原内存被 delete,指向原内存的迭代器变成野指针
  • 删除元素后,被删除位置之后的迭代器可能失效

场景1:insert 扩容导致的失效

cpp 复制代码
// 错误示例
Myvector::vector<int>::iterator p = v1.begin() + 2;
v1.insert(p, 6);      // 如果 insert 触发了扩容,p 变成野指针
*p *= 10;             // ❌ 非法访问!因为p已失效

// 正确做法:重新获取迭代器
p = v1.begin() + 2;   // 重新计算
*p *= 10;             // ✅ 安全

场景2:erase 删除导致的失效

cpp 复制代码
// 错误示例
auto it = v1.begin();
while (it != v1.end())
{
    if (*it % 2 == 0)
    {
        v1.erase(it);   // ❌ erase 后 it 失效,无法再使用
    }
    else
    {
        it++;
    }
}

// 正确做法:使用返回值更新迭代器
auto it = v1.begin();
while (it != v1.end())
{
    if (*it % 2 == 0)
    {
        it = v1.erase(it);  // ✅ 接收返回的新迭代器
    }
    else
    {
        it++;
    }
}

核心原则:

  • insert 和 erase 操作后,原有的迭代器可能失效
  • 如果需要继续使用,必须重新获取或接收返回值

🐶 🐾 ✨ 🐾 🐶


7. 拷贝控制:拷贝构造、赋值运算符、swap

7.1 拷贝构造

cpp 复制代码
vector(const vector<T>& v)
{
    reserve(v.size());                    // 提前开好空间
    for (auto& e : v)                     // 范围 for 遍历
    {
        push_back(e);
    }
}

7.2 swap:交换三个指针

cpp 复制代码
void swap(vector<T>& v)
{
    std::swap(_start, v._start);
    std::swap(_finish, v._finish);
    std::swap(_end_of_storage, v._end_of_storage);
}

7.3 赋值运算符重载(现代写法)

cpp 复制代码
// 现代写法:传值 + swap
vector<T>& operator=(vector<T> v)   // ⚠️ 传值,不是引用
{
    if (this != &v)
    {
        swap(v);                      // 交换内容
    }
    return *this;
}

为什么用传值而不是传引用?

  • 传值时,形参 v 是实参的副本(调用拷贝构造)
  • swap 交换 *this 和 v 的内容
  • 函数结束时,v 被销毁,原 *this 的内容也随之释放
  • 好处:自动处理了深拷贝和异常安全,代码简洁

🐶 🐾 ✨ 🐾 🐶


8. 扩展构造接口

8.1 迭代器区间构造(函数模板)

cpp 复制代码
// 类模板的成员函数,也可以是函数模板
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{
    while (first != last)
    {
        push_back(*first);
        first++;
    }
}

🐾 使用 InputIterator 而不是 iterator,可以接受任意容器的迭代器,不限于 vector 自身。


8.2 n 个 val 构造

cpp 复制代码
vector(size_t n, const T& val = T())
{
    reserve(n);
    for (size_t i = 0; i < n; i++)
    {
        push_back(val);
    }
}

8.3 全局打印函数(typename 的使用)

cpp 复制代码
template <class T>
void print_vector(const vector<T>& v)
{
    // 编译器无法区分 const_iterator 是类型还是静态成员
    // vector<T>::const_iterator it = v.begin();  // ❌ 编译错误

    // 解决方案1:加 typename 告诉编译器这是一个类型
    typename vector<T>::const_iterator it = v.begin();

    // 解决方案2:使用 auto(推荐)
    // auto it = v.begin();

    while (it != v.end())
    {
        cout << *it << " ";
        it++;
    }
    cout << endl;
}

关于 typename :对于未实例化的类模板,编译器无法区分 const_iterator 是类型名还是静态成员变量,需要 typename 关键字显式声明。

🐶 🐾 ✨ 🐾 🐶


9. 完整测试用例

test 1 :增删操作与迭代器失效

cpp 复制代码
void test_vector1()
{
    Myvector::vector<int> v1;
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);
    v1.push_back(5);
    print_vector(v1);           // 1 2 3 4 5

    v1.pop_back();
    print_vector(v1);           // 1 2 3 4

    v1.insert(v1.begin() + 1, 5);
    print_vector(v1);           // 1 5 2 3 4

    // 迭代器失效演示
    Myvector::vector<int>::iterator p = v1.begin() + 2;
    v1.insert(p, 6);
    p = v1.begin() + 2;         // 必须重新获取
    *p *= 10;
    print_vector(v1);           // 1 5 60 2 3 4

    v1.erase(v1.begin() + 2);
    print_vector(v1);           // 1 5 2 3 4

    // 删除所有偶数(使用返回值避免失效)
    auto it = v1.begin();
    while (it != v1.end())
    {
        if (*it % 2 == 0)
        {
            it = v1.erase(it);
        }
        else
        {
            it++;
        }
    }
    print_vector(v1);           // 1 5 3
}

test 2 :拷贝构造与赋值

cpp 复制代码
void test_vector2()
{
    Myvector::vector<int> v1;
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);

    auto v2 = v1;               // 拷贝构造
    print_vector(v2);           // 1 2 3

    Myvector::vector<int> v3;
    v3 = v1;                    // 赋值运算符
    print_vector(v3);           // 1 2 3

    // 自定义类型测试
    Myvector::vector<string> v;
    v.push_back("111");
    v.push_back("222");
    v.push_back("333");
    v.push_back("444");
    v.push_back("555");         // 触发扩容
    print_vector(v);            // 111 222 333 444 555
}

test 2 :迭代器区间构造与 n 个 val 构造

cpp 复制代码
void test_vector3()
{
    Myvector::vector<int> v1;
    for (int i = 1; i <= 5; i++) v1.push_back(i);
    print_vector(v1);           // 1 2 3 4 5

    // 迭代器区间构造
    Myvector::vector<int> v2(v1.begin() + 1, v1.begin() + 4);
    print_vector(v2);           // 2 3 4

    // n 个 val 构造
    Myvector::vector<int> v3(10);
    print_vector(v3);           // 0 0 0 0 0 0 0 0 0 0

    Myvector::vector<int> v4(10u, 1);  // 10u 是 size_t,避免误调迭代器构造
    print_vector(v4);           // 1 1 1 1 1 1 1 1 1 1
}

🐶 🐾 ✨ 🐾 🐶


总结

模块 关键实现
内存管理 三指针架构: _start、_finish、_end_of_storage
迭代器 原生指针 T* 封装
扩容 reserve + 深拷贝(逐元素赋值,不能用 memcpy)
插入删除 insert/erase + 迭代器失效处理
拷贝控制 拷贝构造、现代写法赋值运算符(传值 + swap)
类型萃取 T() 获取类型默认值、typename 解决依赖类型

迭代器失效总结:

  • insert 扩容后,所有迭代器都可能失效
  • erase 后,被删除位置之后的迭代器失效
  • 解决方案:重新获取或接收返回值

🐶 🐾 ✨ 🐾 🐶


本文全部代码

🐾 vector.h

cpp 复制代码
#pragma once
#include<iostream>
#include<assert.h>
#include<string>
using namespace std;

			//三指针控制内存
			//vector本质是动态管理连续内存的容器,其核心是通过三个内存指针(迭代器)来控制内存块:
			//_start:指向内存块的起始位置(第一个元素的地址)
			//_finish:指向有效元素的末尾(最后一个元素的下一位)
			//_end_of_storage:指向内存块的,末尾(容量的边界)

namespace Myvector
{
				//template<>是模板,T是占位符,用它替代具体的int、string、double类型
	template<class T>

	class vector
	{
	public:
		typedef T* iterator;		//将T*重命名为iterator(迭代器),也就是对原生指针的封装
		typedef const T* const_iterator;

				//通过这三个指针,可以间接计算有效元素的个数(size),容量(capacity),就不用像数组那样麻烦
		size_t size()const
		{
			return _finish - _start;			//有效元素个数
		}

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

		//无参构造
		vector() 
						// _start:指向内存块的起始位置(第一个元素的地址)
						//_finish:指向有效元素的末尾(最后一个元素的下一位)
						//_end_of_storage:指向内存块的,末尾(容量的边界)

			:_start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
						//这里写不写初始化列表都无所谓,因为已经有了缺省值
		{

		}

					//v1(v2)
					//拷贝构造---v2拷贝给v1
					//reserve:仅扩容,不初始化元素

		vector(const vector<T>& v)			//引用,vector<T>是类型
		{
			reserve(v.size());	//提前开好空间,避免后面push_back不断扩容
			for (auto& e : v)
			{
				push_back(e);
			}
		}

		//清空数据clear
		void clear()
		{
			_finish = _start;
			//只清空数据不改变空间大小
		}

				//v3 = v1
				//赋值运算符重载
				//传统写法
				//vector<T>& operator=(const vector<T>& v)
				//{
				//	if (this != &v)//地址相同则说明在同一块空间不需要赋值
				//	{
				//		clear();
				//		reserve(v.size());
				//		for (auto& e : v)
				//		{
				//			push_back(e);
				//		}
				//	}
				//	return *this;
				//}

		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_end_of_storage, v._end_of_storage);
		}

		//现代写法
		vector<T>& operator=(vector<T> v)
			//需要注意的是:现代写法的形参不能和传统写法一样是引用传参
			//因为swap会对形参和*this进行内容交换,如果是引用传参则外部的实参也会受到影响
			//而传值传参时形参不管怎么改变出了函数就会被销毁,不会影响实参
			//而且如果*this没有初始化的话则会更加危险
		{
			if (this != &v)//地址相同则说明在同一块空间不需要赋值
			{
				swap(v);
			}
			return *this;
		}

		//析构
		~vector()
		{
			if (_start) //如果_start不就是空则不需要delete
			{
				delete[] _start;
				_start = _finish = _end_of_storage = nullptr;
			}
		}

		void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t old_size = size();	//先把保存原大小
				T* tmp = new T[n];
				//memcpy(tmp, _start, size() * sizeof(T));
				//对于内置类型没有问题,但是自定义类型就会进行浅拷贝而出现问题
				for (size_t i = 0; i < old_size; i++)
				{
					tmp[i] = _start[i];
				}//深拷贝

				delete[] _start;	//释放掉原数组,还没给他赋新值,_finish 仍是原数组的指针,size()就不能用了
				_start = tmp;		//赋新值了,_start指向了新数组
				_finish = _start + old_size;	//finish要想不是空指针就得用原大小(old size)+新的_start
				_end_of_storage = _start + n;


						////_finish 会空指针版本
						//if (n > capacity())
						//{
						//	T* tmp = new T[n];
						//	mecoy(tmo, _start, size() * sizeof(T));
						//	delete[] _start;
						//	_start = tmp;
						//	_finish = _start + size();
						//	_end_storage=_start+n;
						//}
			}
		}

			//resize(n,val)会将size调整为n,多余元素销毁,不足则用val填充
		void resize(size_t n, T val = T())
			//=T()表示:如果调用时不写第二个参数,就要用T类型的默认构造对象来填充

			//由于val类型是T,此时就不能像以前用0来充当缺省值,如何理解这句话呢?
			//   当T是int类型时,T()就是0
			//	 当T是string\vector这种自定义类型时,0不能用来初始化,必须用T()调用他自己的默认构造函数
			
		{
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				if (n > capacity())
				{
					reserve(n);
				}
				while (_finish < _start + n)		//元素不足时
				{
					*_finish = val;
					_finish++;
				}
			}
		}

		T& operator[](size_t i)
		{
			assert(i < size());
			return _start[i];
		}

		const T& operator[](size_t i) const
		{
			assert(i < size());
			return _start[i];
		}

		//尾插push_back
		void push_back(const T& x)
		{
			if (_finish == _end_of_storage)
			{
				reserve(capacity() == 0 ? 4 : 2 * capacity());
			}
			*_finish = x;
			_finish++;
		}

		//判空
		bool empty()
		{
			return _start == _finish;
		}

		//尾删pop_back
		void pop_back()
		{
			assert(!empty());
			--_finish;
		}

		//插入insert
		void insert(iterator pos, const T& x)
		{
			assert(pos >= _start && pos < _finish);
			if (_finish == _end_of_storage)
			{
				size_t len = pos - _start;	//提前获取偏移量

				reserve(capacity() == 0 ? 4 : 2 * capacity());

				pos = _start + len;			//对pos进行修正(也就是重新获取迭代器)
			}
			iterator end = _finish - 1;		//end指向的位置是原数组最后一位有效数字的位置

			while (end >= pos)
			{
				*(end + 1) = *end;			//从后往前赋值,避免后面的值被前面的值覆盖
				end--;
			}
			*pos = x;
			_finish++;
		}

		//删除erase
		iterator erase(iterator pos)
		{
			assert(pos >= _start && pos < _finish);
			iterator it = pos + 1;
			while (it != end())
			{
				*(it - 1) = *it; //从前往后赋值
				it++;
			}
			_finish--;
			return pos;
		}

		//迭代器
		iterator begin()
		{
			return _start;
		}

		iterator end()
		{
			return _finish;
		}

		const_iterator begin() const
		{
			return _start;
		}

		const_iterator end() const
		{
			return _finish;
		}

		//补充接口
		//迭代器区间构造
		//类模板的成员函数,也可以继续是函数模板
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
			//不用iterator迭代器的好处是:不仅限于传vector的迭代器,其他容器的迭代器均可以传入
		{
			while (first != last)
			{
				push_back(*first);
				first++;
			}
		}

		//n个val构造
		vector(size_t n, const T& val = T())
		{
			reserve(n);
			for (size_t i = 0; i < n; i++)
			{
				push_back(val);
			}
		}

	private:
		iterator _start = nullptr;//数据起始位置
		iterator _finish = nullptr;//有效数据的结束位置
		iterator _end_of_storage = nullptr;//总空间大小的结尾位置
	};

	//迭代器遍历
	template <class T>
	void print_vector(const vector<T>& v)
	{
		//vector<T>::const_iterator it = v.begin(); //error
		//规定:没有实例化的类模板编译器是不能在里面获取东西。
		//编译器无法区分 const_iterator 究竟是我们 typedef 出的类型还是静态成员变量
		typename vector<T>::const_iterator it = v.begin();
		//auto it = v.begin(); //用auto就可以省去这些麻烦了,可以直接自动推导出对应类型
		while (it != v.end())
		{
			cout << *it << " ";
			it++;
		}
		cout << endl;
	}

}

🐾 Test.cpp

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include "vector.h"

//增删
void test_vector1()
{
	Myvector::vector<int> v1;
	//v1.reserve(v1.capacity() == 0 ? 4 : 2 * v1.capacity());
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);
	v1.push_back(5);

	//遍历
	for (size_t i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";////1 2 3 4 5

	}
	//print_vector(v1);	//1 2 3 4 5----替代上面一整段

	cout << endl;

	//尾删
	v1.pop_back();
	print_vector(v1);	//1 2 3 4

	//insert插入---在第2个位置插入
	v1.insert(v1.begin() + 1, 5);
	print_vector(v1);	//1 5 2 3 4

	//迭代器失效:扩容-----在第3个位置插入
	Myvector::vector<int>::iterator p = v1.begin() + 2;
	v1.insert(p, 6);

	////当 insert 如果扩容了,则第一个实参迭代器就会因为_start被delete而失效(变成野指针)
	////此时就不能再次访问,如果需要访问就需要重新更新失效迭代器的值
	p = v1.begin() + 2;
	*p *= 10;			//6*10,上面的也会变成60
	print_vector(v1);	//1 5 60 2 3 4

	//erase删除
	v1.erase(v1.begin() + 2);
	print_vector(v1);	//1 5 2 3 4

	//迭代器失效:删除
	//删除所有偶数
	auto it = v1.begin();
	while (it != v1.end())
	{
		if (*it % 2 == 0)
		{
			it = v1.erase(it);// 用返回值更新迭代器
		}
		else
		{
			it++;
		}
	}
	print_vector(v1);

}

//int main()
//{
//	test_vector1();
//	return 0;
//}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void test_vector2()
{
	////int型
	Myvector::vector<int> v1;

	//尾插
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);

	//拷贝构造
	auto v2 = v1; 
	print_vector(v1);	//1 2 3
	print_vector(v2);	//1 2 3

	Myvector::vector<int> v3;
	v3 = v1; //赋值运算符重载
	print_vector(v3);	//1 2 3

	//自定义类型
	Myvector::vector<string> v;
	v.push_back("111");
	v.push_back("222");
	v.push_back("333");
	v.push_back("444");
	print_vector(v);

	v.push_back("555"); //此时需要扩容
	print_vector(v);


}

//int main()
//{
//	test_vector2();
//	return 0;
//}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void test_vector3()
{
	Myvector::vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);
	v1.push_back(5);
	print_vector(v1);	//1 2 3 4 5

	//迭代器区间构造
	Myvector::vector<int> v2(v1.begin() + 1, v1.begin() + 4);	//[第2个位置,第4个位置)
	print_vector(v2);	//2 3 4

	//n个val构造
	Myvector::vector<int> v3(10);
	print_vector(v3);	//0 0 0 0 0 0 0 0 0 0

	Myvector::vector<int> v4(10u, 1); //10u的类型就是size_t,就不会误调用迭代器区间构造,元素是1
	print_vector(v4);	//1 1 1 1 1 1 1 1 1 1

}

//int main()
//{
//	test_vector3();
//
//	return 0;
//}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//int main()
//{
//	//test_vector1();
//	//test_vector2();
//	//test_vector3();
//	int a = int();  //无参则为0
//	int b = int(1);
//	int c(b);		//用变量b的值初始化c
//	cout << a << " " << b << " " << c << " " << endl;//0 1 1
//	return 0;
//}
  1. 欢迎留言交流
  2. 期待你的评论与建议
  3. 留下你的想法吧

谢谢你看到这里呀

如果喜欢这篇内容,点个关注,下次更新不迷路✨

👍 点赞 ⭐ 收藏 💬 评论

相关推荐
a诠释淡然1 小时前
C++ vs Rust:哪个更适合你的下一个项目?
开发语言·c++·rust
meilindehuzi_a1 小时前
深入理解 JavaScript 执行机制:从编译阶段到调用栈底层实现
开发语言·javascript·ecmascript
小小de风呀1 小时前
de风——【从零开始学C++】(十二):stack和queue的基本使用和模拟实现
开发语言·c++
元直数字电路验证1 小时前
云计算实验笔记(四):容器编排(Container Orchestration)
运维·笔记·docker·云计算
huohaiyu1 小时前
深入解析Java垃圾回收机制
java·开发语言·算法·gc
汉克老师1 小时前
GESP6级C++考试语法知识(五十三、动态规划----背包问题(六、分组背包)
c++·动态规划·背包问题·gesp6级·gesp六级·分组背
YsyaaabB1 小时前
LangChain作业二---多语言翻译Prompt
开发语言·python·langchain
SunnyDays10111 小时前
如何在 Java 中实现 OFD 与 PDF 格式互转
java·开发语言
nashane2 小时前
HarmonyOS 6学习:句柄泄漏(Fd Leak)从“崩溃现场”到“代码行”的精准狙击指南
学习·华为·音视频·harmonyos