STL之vector篇(下)(手撕底层代码,从零实现vector的常用指令,深度剖析并优化其核心代码)

文章目录

    • 1.基本结构与初始化
      • 1.1 空构造函数的实现与测试
      • 1.2 带大小和默认值的构造函数
      • 1.3 使用迭代器范围初始化的构造函数(建议先看完后面的reserve和push_back)
      • 1.4 拷贝构造函数
      • 1.5 赋值操作符的实现(深拷贝)
      • 1.6 析构函数
      • 1.7 `begin` 与 `end` 迭代器
    • 2. 容量管理
      • 2.1 `reserve`函数:实现动态扩容
      • 2.2 `resize`函数:改变大小
    • 3. 元素插入与删除
      • 3.1 `push_back`函数:尾插元素
      • 3.2 `pop_back`函数:尾删元素
      • 3.3 `insert`函数:在指定位置插入元素
      • 3.4 `erase`函数:删除指定位置的元素
    • 4.全部代码实现
      • 4.1 头文件
      • 4.2 源文件


前言:

在编程的浩瀚星空中,数据结构如同指引方向的星辰,它们不仅是构建算法与程序大厦的基石,更是解决复杂问题、优化性能的关键所在。其中,vector作为C++标准模板库(STL)中的一颗璀璨明珠,凭借其灵活、高效的特点,在软件开发中占据了举足轻重的地位。

vector,即向量容器,是一种能够存储任意类型对象的动态数组。它封装了动态数组的所有特性,包括自动的内存管理、灵活的元素访问方式以及高效的随机访问性能,同时还提供了丰富的成员函数来支持元素的插入、删除、遍历等操作。与静态数组相比,vector的最大优势在于其大小可以动态变化,无需程序员手动管理内存,极大地简化了编程复杂度,提高了代码的安全性和可维护性。
本文旨在深入探讨vector的内部机制、使用技巧及最佳实践,帮助读者全面了解并掌握这一强大的数据结构。我们将从vector的基本概念和特性出发,逐步深入到其内存管理策略、迭代器使用、元素操作、性能优化等多个方面。同时,结合实际应用场景,展示vector在解决各种编程问题时的独特魅力和高效性。

无论你是C++编程的初学者,还是希望提升编程技能的老手,本文都将为你提供宝贵的参考和实用的指导。让我们一起踏上这场关于vector的探索之旅,解锁C++编程的新世界,感受数据结构的魅力与力量。

1.基本结构与初始化

1.1 空构造函数的实现与测试

  • 实现一个空的vector,不分配任何内存。
  • 测试是否创建了容量为0的vector

实现代码:

namespace xny {
    template<class T>
    class vector {
    public:
        typedef T* iterator;

        vector() {}

        size_t size() const {return _finish - _start;}
        size_t capacity() const {return _endofstorage - _start;}
        bool empty() const {return _start == _finish;}

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

测试样例:

void testvector1() {
    xny::vector<int> v;
    assert(v.size() == 0);  // 验证大小
    assert(v.capacity() == 0);  // 验证容量
    assert(v.empty());  // 验证是否为空
    cout << "testvector1 passed" << endl;
}

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

输出:

testvector1 passed

1.2 带大小和默认值的构造函数

  • 初始化一个给定大小的vector,并使用默认值填充。
  • 测试构造后大小、容量是否符合要求。

实现代码:

namespace xny {
    template<class T>
    class vector {
    public:
        typedef T* iterator;

        vector() {}

        vector(size_t n, const T& val = T()){    // 缺省参数用T类型的匿名对象
            _start = new T[n];
            _finish = _start + n;
            _endofstorage = _finish;
            for (size_t i = 0; i < n; i++) {
                _start[i] = val;  // 填充默认值
            }
        }

        T& operator[](size_t pos) { return _start[pos]; }

        size_t size() const {return _finish - _start;}
        size_t capacity() const {return _endofstorage - _start;}
        bool empty() const {return _start == _finish;}

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

测试样例:

void testvector2(){
    xny::vector<int> v(4, 1);
    assert(v.size() == 4);  // 验证大小
    assert(v.capacity() == 4);  // 验证容量
    assert(!v.empty());  // 验证是否为空
    for (size_t i = 0; i < 4; ++i) {
        assert(v[i] == 1);  // 验证默认值
    }
    cout << "testvector2 passed" << endl;
}

int main()
{
    testvector3();

    return 0;
}

输出:

testvector2 passed

1.3 使用迭代器范围初始化的构造函数(建议先看完后面的reserve和push_back)

【思考】:

  • 假设我用上述代码去实现以下样例会发生什么呢?
void testvector3() {
    int arr[] = { 1, 2, 3, 4, 5 };
    xny::vector<int> v(arr, arr + 5); // 使用迭代器范围初始化构造函数
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

【结果】:

  • 这时我们意识到可能需要重新定义一个构造函数,并且这个构造函数可以使用迭代器范围初始化。

添加代码如下:

namespace xny {
    template<class T>
    class vector {
    public:
        typedef T* iterator;

        iterator begin(){ return _start; }
        iterator end(){ return _finish; }

        vector() {}

        vector(size_t n, const T& val = T()){    // 缺省参数用T类型的匿名对象
            _start = new T[n];
            _finish = _start + n;
            _endofstorage = _finish;
            for (size_t i = 0; i < n; i++) {
                _start[i] = val;  // 填充默认值
            }
        }
        
		// 使用迭代器范围初始化的构造函数
        template<class InputIterator>
        vector(InputIterator first, InputIterator last){
            while (first != last)	// 迭代器区间左闭右开
            {
                push_back(*first);
                ++first;
            }
        }

        void reserve(size_t n){
            if (n > capacity()){
                size_t sz = size();
                T* tmp = new T[n];
                if (_start){
                    for (size_t i = 0; i < sz; i++){
                        tmp[i] = _start[i];
                    }
                    delete[] _start;
                }

                _start = tmp;
                _finish = _start + sz;
                _endofstorage = _start + n;
            }
        }

        void push_back(const T& x){
            if (_finish == _endofstorage){
                size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
                reserve(newcapacity);
            }

            *_finish = x;
            ++_finish;
        }

        T& operator[](size_t pos) { return _start[pos]; }

        size_t size() const {return _finish - _start;}
        size_t capacity() const {return _endofstorage - _start;}
        bool empty() const {return _start == _finish;}

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

测试样例:

void testvector3() {
    int arr[] = { 1, 2, 3, 4, 5 };
    xny::vector<int> v1(arr, arr + 5); // 使用迭代器范围构造函数
    for (auto e : v1){
        cout << e << " ";
    }
    cout << endl;

    string str("hello world");
    xny::vector<char> v2(str.begin(), str.end());
    for (auto e : v2) {
        cout << e << " ";
    }
    // 这里我们用两种不同类型的迭代器进行测试
}

输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

【思考】:

  • 怎么回事呢?编译器又报错了,并且这次报的错比较模糊,于是我们可以直接搜索error C2100,看看编译示例:

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

【解释】:

  • 结合以上所有的编译报错我们终于发现错误的原因可能是非法寻址了,于是再次进行改进添加代码如下:
vector(int n, const T& val = T()) {    
    _start = new T[n];
    _finish = _start + n;
    _endofstorage = _finish;
    for (size_t i = 0; i < n; i++) {
        _start[i] = val; 
    }
}

输出:

1 2 3 4 5
h e l l o   w o r l d

1.4 拷贝构造函数

  • 实现vector的拷贝构造函数。
  • 测试拷贝后的vector是否完全复制原来的内容和容量。

实现代码:

namespace xny {
    template<class T>
    class vector {
    public:
        typedef T* iterator;
        
        vector() {}
        
        vector(size_t n, const T& val = T()){    
            _start = new T[n];
            _finish = _start + n;
            _endofstorage = _finish;
            for (size_t i = 0; i < n; i++) {
                _start[i] = val;  
            }
        }
        
        vector(const vector<T>& v){
            size_t sz = v.size();   
            _start = new T[capacity()];
            for (size_t i = 0; i < sz; i++) {
                _start[i] = v._start[i];
            }

            _finish = _start + sz;
            _endofstorage = _start + v.capacity();
        }

        T& operator[](size_t pos) { return _start[pos]; }

        size_t size() const {return _finish - _start;}
        size_t capacity() const {return _endofstorage - _start;}
        bool empty() const {return _start == _finish;}

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

测试样例:

void testvector4() {
    xny::vector<int> v1(5, 10);
    xny::vector<int> v2(v1);
    assert(v2.size() == 5);  // 验证大小
    assert(v2.capacity() == 5);  // 验证容量
    for (size_t i = 0; i < 5; ++i) {
        assert(v2[i] == 10);  // 验证数据拷贝
    }
    std::cout << "testvector4 passed" << std::endl;
}

int main()
{
    testvector4();

    return 0;
}

输出:

testvector4 passed

1.5 赋值操作符的实现(深拷贝)

  • 实现赋值操作符重载。
  • 测试两个vector赋值后,是否正确拷贝了内容和容量。

实现代码:

namespace xny {
    template<class T>
    class vector {
    public:
        typedef T* iterator;
        
        vector() {}

        vector(size_t n, const T& val = T()){    
            _start = new T[n];
            _finish = _start + n;
            _endofstorage = _finish;
            for (size_t i = 0; i < n; i++) {
                _start[i] = val;  
            }
        }
        
		// 自定义一个swap函数方便拷贝
        void swap(const vector<T> v){
            std::swap(_start, v._start);
            std::swap(_finish, v._finish);
            std::swap(_endofstorage, v._endofstorage);
        }
		
        // 赋值运算符重载
        vector<T>& operator=(const vector<T>& v){
            swap(v);
            return *this;
        }

        T& operator[](size_t pos) { return _start[pos]; }

        size_t size() const {return _finish - _start;}
        size_t capacity() const {return _endofstorage - _start;}
        bool empty() const {return _start == _finish;}

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

【说明】:

  • **传值参数:**通过传递 vector<T> 的值作为参数,创建一个临时对象 v。调用拷贝构造函数时自动执行拷贝,然后在赋值操作中与现有对象交换内容。传值是安全的,避免了手动内存管理问题。
  • **swap函数:**通过交换数据成员,避免手动内存释放,简化代码逻辑。交换后的临时对象 v 离开作用域时自动销毁,保证资源释放。

测试样例:

void testvector5() {
    xny::vector<int> v1(5, 10);
    xny::vector<int> v2 = v1;  // 赋值操作
    assert(v2.size() == 5);  // 验证大小
    assert(v2.capacity() == 5);  // 验证容量
    for (size_t i = 0; i < 5; ++i) {
        assert(v2[i] == 10);  // 验证数据拷贝
    }
    std::cout << "testvector5 passed" << std::endl;
}

int main()
{
    testvector5();

    return 0;
}

输出:

testvector5 passed

1.6 析构函数

实现代码:

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

【注意】

  • 为了避免内存泄露的问题,上述代码全部都需要添加析构函数。

1.7 beginend 迭代器

【解释】:

  • begin 函数返回指向 vector 起始位置的迭代器(即指向第一个元素)。
  • end 函数返回指向 vector 末尾的迭代器(即指向最后一个元素的下一个位置)。
  • 两者结合可以用于遍历 vector 中的元素。

添加代码:

iterator begin(){
	return _start;
}

iterator end(){
	return _finish;
}

2. 容量管理

2.1 reserve函数:实现动态扩容

  • 实现reserve函数,测试在容量不足时是否能正确扩展。

添加代码:

void reserve(size_t n) {
    if (n > capacity()) {
        size_t sz = size();
        T* tmp = new T[n];

        if (_start) {
            for (size_ t i = 0; i < sz; i++) {
                tmp[i] = _start[i];
            }
            delete[] _start;
        }

        _start = tmp;
        _finish = _start + sz;
        _endofstorage = _start + n;
    }
}

【注意】:

  • T是自定义类型时,依次调用数组每个对象的析构函数,再释放整个空间

  • 原来for循坏中使用的是memcpy(tmp, _start, sizeof(T) * sz);但是vector是深拷贝,但是vector空间上存的对象是string数组,使用memcpy导致string对象的浅拷贝

  • 解决方案:T是string这样深拷贝的类,调用的是string赋值重载,实现的是string对象的深拷贝

测试用例:

void testvector6() {
    xny::vector<int> v(5, 10);
    v.reserve(10);  // 预留容量
    assert(v.capacity() == 10);  // 验证容量扩展
    for (size_t i = 0; i < 5; ++i) {
        assert(v[i] == 10);  // 验证数据保持不变
    }
    std::cout << "testvector6 passed" << std::endl;
}

int main()
{
    testvector6();

    return 0;
}

输出:

testvector6 passed

2.2 resize函数:改变大小

  • 实现resize函数,测试增加或减少vector大小,如果增加,用特定字符暂时覆盖。

添加代码:

void resize(size_t n, const T& val = T()) {
    if (n < size()) {
        _finish = _start + n;
    }
    else {
        // 扩容n
        reserve(n);

        // 传值
        while (_finish != _start + n) {
            *_finish = val;
            ++_finish;
        }
    }
}

测试用例:

void testvector7() {
    xny::vector<int> v(5, 10);
    v.resize(8, 20);  // 扩展大小并填充新值
    assert(v.size() == 8);  // 验证扩展后大小
    for (size_t i = 0; i < 5; ++i) {
        assert(v[i] == 10);  // 验证原值不变
    }
    for (size_t i = 5; i < 8; ++i) {
        assert(v[i] == 20);  // 验证新值
    }
    std::cout << "testvector7 passed" << std::endl;
}

int main()
{
    testvector7();

    return 0;
}

输出:

testvector7 passed

3. 元素插入与删除

3.1 push_back函数:尾插元素

【实现步骤】:

    1. 检查容量是否足够,若不足则扩容(通常容量加倍)。
    1. 将新元素插入到当前末尾。
    1. 更新_finish指针,指向新的末尾。

添加代码:

void push_back(const T& val){
    if (_finish == _endofstorage) {
        size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;  // 采用2倍扩容
        reserve(newcapacity);
    }

    *_finish = val;
    ++_finish;
}

优化版本:

void push_back(const T& x){
	insert(end(), x);
}

测试用例:

void testvector8() {
    xny::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);

    assert(v.size() == 3);  // 验证插入后的大小
    assert(v.capacity() >= 3);  // 验证容量是否自动扩展
    assert(v[0] == 1 && v[1] == 2 && v[2] == 3);  // 验证插入的元素是否正确

    std::cout << "testvector9 passed" << std::endl;
}

int main()
{
    testvector8();

    return 0;
}

输出:

testvector8 passed

3.2 pop_back函数:尾删元素

【实现步骤】:

    1. _finish指针向前移动一位,即删除最后一个元素。
    1. 不释放空间。

添加代码:

void pop_back(const T& val) {
    assert(_finish != _start);  // 确保vector非空
    --_finish;  // 逻辑删除最后一个元素
}

优化版本:

void pop_back(){
	erase(--end());
}

测试用例:

void testvector9() {
    xny::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.pop_back();

    assert(v.size() == 2);  // 验证删除后的大小
    assert(v[0] == 1 && v[1] == 2);  // 验证剩余元素是否正确

    std::cout << "testvector9 passed" << std::endl;
}

int main()
{
    testvector9();

    return 0;
}

输出:

testvector9 passed

3.3 insert函数:在指定位置插入元素

【实现步骤】:

    1. 检查容量是否足够,不足时扩容。
    1. 将插入位置及其后的元素整体向后移动。
    1. 将新元素插入指定位置。
    1. 更新_finish指针。

添加代码:

iterator insert(iterator pos, const T& val) {
    // 断言判断pos的有效性
    assert(pos >= _start && pos <= _finish);

    if (_finish == _endofstorage) {
        // 用临时变量存指定位置到起点的长度
        size_t len = pos - _start;
        size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;  // 采用2倍扩容
        reverse(newcapacity);

        // 解决pos迭代器失效问题
        pos = len + _start;
    }

    // 将插入位置及其后的元素整体向后移动。
    iterator end = _finish - 1;
    while (end >= pos) {
        *(end + 1) = *end;
        --end;
    }

    *pos = val;
    ++_finish;

    return pos;
}

测试用例:

void testvector10() {
    xny::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(4);

    v.insert(v.begin() + 2, 3);  // 在第2个位置插入3

    assert(v.size() == 4);  // 验证插入后的大小
    assert(v[0] == 1 && v[1] == 2 && v[2] == 3 && v[3] == 4);  // 验证插入的元素是否正确

    std::cout << "testvector8 passed" << std::endl;
}

int main()
{
    testvector10();

    return 0;
}

输出:

testvector10 passed

【问题】:

  • 扩容之后,pos迭代器可能会发生改变,所以不能用原来的pos

3.4 erase函数:删除指定位置的元素

【实现步骤】:

    1. erase位置之后的元素向前移动一位。
    1. 更新_finish指针。

添加代码:

iterator erase(iterator pos) {
    // 断言判断pos的有效性
    assert(pos >= _start && pos <= _finish);

    iterator it = pos + 1;
    while (it != _finish) {
        *(it - 1) = *it;
        ++it;
    }
    --_finish;

    return pos;
}

测试用例:

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

    v.erase(v.begin() + 2);  // 删除第2个元素

    assert(v.size() == 3);  // 验证删除后的大小
    assert(v[0] == 1 && v[1] == 2 && v[2] == 4);  // 验证删除后的元素顺序

    std::cout << "testvector11 passed" << std::endl;
}

int main()
{
    testvector11();

    return 0;
}

输出:

testvector11 passed

4.全部代码实现

【注意】

  • 以下代码会提供常量和普通变量的两种函数或者迭代器。

4.1 头文件

#pragma once

#include<assert.h>
using namespace std;

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

		iterator begin()
		{
			return _start;
		}

		iterator end()
		{
			return _finish;
		}

		const_iterator begin() const
		{
			return _start;
		}

		const_iterator end() const
		{
			return _finish;
		}

		vector(size_t n, const T& val = T())
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			resize(n, val);
		}

		// 解决vector<int> v(10, 1)
		vector(int n, const T& val = T())
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			resize(n, val);
		}

		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)	// 迭代器区间左闭右开
			{
				push_back(*first);
				++first;
			}
		}

		vector()
		{}

		vector(const vector<T>& v)
		{
			size_t sz = v.size();
			_start = new T[v.capacity()];
			// memcpy(_start, v._start, sizeof(T) * v.size());
			for (size_t i = 0; i < sz; i++)
			{
				_start[i] = v._start[i];
			}

			_finish = _start + sz;
			_endofstorage = _start + v.capacity();
		}

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

		// v1 = v2
		vector<T>& operator=(vector<T> v)
		{
			swap(v);
			return *this;
		}

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


		void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t sz = size();
				T* tmp = new T[n];
				if (_start)
				{
					for (size_t i = 0; i < sz; i++)
					{
						tmp[i] = _start[i];
					}
					delete[] _start;
				}

				_start = tmp;
				_finish = _start + sz;
				_endofstorage = _start + n;
			}
		}

		void resize(size_t n, const T& val = T())	// T类型的匿名对象
		{
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				reserve(n);

				while (_finish != _start + n)
				{
					*_finish = val;
					++_finish;
				}
			}
		}

		void push_back(const T& x)
		{
			/*if (_finish == _endofstorage)
			{
				size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newcapacity);
			}

			*_finish = x;
			++_finish;*/
			insert(end(), x);
		}

		void pop_back()
		{
			erase(--end());
		}

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

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

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

			return _start[pos];
		}

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

			return _start[pos];
		}

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

			if (_finish == _endofstorage)
			{
				size_t len = pos - _start;

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

				// 解决pos迭代器失效问题
				pos = _start + len;
			}

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

			*pos = x;
			++_finish;

			return pos;
		}

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

			iterator it = pos + 1;
			while (it != _finish)
			{
				*(it - 1) = *it;
				++it;
			}
			--_finish;

			return pos;
		}

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

	void print(const vector<int>& v)
	{
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	void test_vector1()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);
		v1.push_back(5);

		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;

		for (size_t i = 0; i < v1.size(); i++)
		{
			v1[i]++;
		}

		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;

		print(v1);
	}

	void test_vector2()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);
		v1.push_back(5);
		v1.push_back(5);
		v1.push_back(5);
		v1.push_back(5);

		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;

		v1.insert(v1.begin(), 100);

		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;

		/*vector<int>::iterator p = v1.begin() + 3;
		v1.insert(p, 300);*/

		xny::vector<int>::iterator p = v1.begin() + 3;
		//v1.insert(p+3, 300);

		// insert以后迭代器可能会失效(扩容)
		// 记住,insert以后就不要使用这个形参迭代器了,因为他可能失效了
		v1.insert(p, 300);

		// 高危行为
		// *p += 10;

		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;
	}

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

		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;

		// v1.erase(v1.begin());

		auto it = v1.begin() + 4;
		v1.erase(it);

		// erase之后,迭代器失效了,不能访问
		// vs进行强制检查,访问会直接报错
		cout << *it << endl;
		++it;
		cout << *it << endl;

		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	void test_vector4()
	{
		vector<int> v;
		v.resize(10, 0);

		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;

	}

	void test_vector5()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);
		v1.push_back(5);

		vector<int> v2(v1);

		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;

		vector<int> v3 = v1;
		for (auto e : v3)
		{
			cout << e << " ";
		}
		cout << endl;
	};

	void test_vector6()
	{
		vector<string> v;
		v.push_back("11111");
		v.push_back("22222");
		v.push_back("33333");
		v.push_back("44444");
		v.push_back("55555");

		// 自定义类型最好加引用
		for (auto& e : v)
		{
			cout << e << " ";
		}
		cout << endl;

		vector<string> v1(v);
		for (auto& e : v1)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	void test_vector7()
	{
		vector<int> v(10u, 1);
		vector<string> v1(10, "111");

		// vector<int> v2(10, 1);

	}
}

4.2 源文件

#include"vector.h"
#include <bits/stdc++.h>
using namespace std;

int main()
{
	xny::test_vector7();
	// ...其他测试
    
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/883656.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用宝塔部署项目在win上

项目部署 注意&#xff1a; 前后端部署项目&#xff0c;需要两个域名&#xff08;二级域名&#xff0c;就是主域名结尾的域名&#xff0c;需要在主域名下添加就可以了&#xff09;&#xff0c;前端一个&#xff0c;后端一个 思路&#xff1a;访问域名就会浏览器会加载前端的代…

如何守护变美神器安全?红外热像仪:放开那根美发棒让我来!

随着智能家电市场的迅速发展&#xff0c;制造商们越来越关注生产过程中效率和质量的提升。如何守护变美神器安全&#xff1f;红外热像仪&#xff1a;放开那根卷发棒让我来&#xff01; 美发棒生产遇到什么困境&#xff1f; 美发棒生产过程中会出现设备加热不均情况&#xff0c…

[数据库实验五] 审计及触发器

一、实验目的与要求&#xff1a; 1.了解MySQL审计功能及实现方式 2.掌握触发器的工作原理、定义及操作方法 二、实验内容&#xff1a; 注&#xff1a; 在同一个触发器内编写多行代码&#xff0c;需要用结构begin ……end 函数current_user()获得当前登录用户名 1.自动保存…

智慧城市主要运营模式分析

(一)运营模式演变 作为新一代信息化技术落地应用的新事物,智慧城市在建设模式方面借鉴了大量工程建设的经验,如平行发包(DBB,Design-Bid-Build)、EPC工程总承包、PPP等模式等,这些模式在不同的发展阶段和条件下发挥了重要作用。 在智慧城市发展模式从政府主导、以建为主、…

linux----进程地址空间

前言 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、空间分布 二、栈和堆的特点 &#xff08;1&#xff09;栈堆相对而生&#xff0c;堆是向上增长的&#xff0c;栈是向下增长的。 验证&#xff1a;堆是向上增长的 这里我们看到申请的堆&#xff…

记一次Windows状态栏不显示问题

文章目录 &#x1fa9f;解决方案☁️单次处理☁️有效处理 &#x1fa9f;现象&#x1fa9f;尝试的操作⭐END&#x1f31f;跋&#x1f31f;交流方式 &#x1fa9f;解决方案 ☁️单次处理 重启explorer.exe 命令行操作 注意&#xff0c;使用命令行操作的时候&#xff0c;出现…

链动 2+1 模式 S2B2C 商城小程序源码:创新价格盈利模式探索

摘要&#xff1a;本文深入探讨了价格盈利模式的两种类型&#xff0c;即价格返利模式和动态定价盈利模式。通过引入链动 21 模式 S2B2C 商城小程序源码&#xff0c;分析其在实现这两种价格盈利模式方面的优势和应用场景&#xff0c;为朋友圈卖货及电商领域的发展提供新的思路和方…

QT菜单之快捷菜单设计

快捷菜单又称为上下文菜单&#xff0c;通常在用鼠标右击的时候弹出。创建快捷菜单的方法和创建菜单栏菜单类似。 效果图&#xff1a; 一、将MainWindow类对象的ContextMenuPolicy属性设置为customContextMenu。 打开mainWindow.ui&#xff0c;在属性视图上找到ContextMenuPoli…

一文掌握python单元测试unittest(二)

接上篇:https://blog.csdn.net/qq_38120851/article/details/141642215 目录 四、参数化测试 1、使用 subTest 2、使用装饰器 3)使用第三方库parameterized 五、跳过测试 1、使用 unittest.skip() 或 unittest.skipIf() 装饰器: 2、使用 setUp() 方法中的断言来跳过整…

EasyCVR智慧公园视频智能管理方案:赋能公园安全管理新高度

随着城市化进程的加速&#xff0c;智慧城市建设已成为提升城市管理效率、增强居民生活质量的重要途径。智慧公园作为智慧城市的重要组成部分&#xff0c;其安全与管理水平直接影响着市民的休闲娱乐体验。EasyCVR智慧公园视频智能管理方案&#xff0c;正是基于这一背景应运而生&…

Android 车载应用开发指南 - CarService 详解(下)

车载应用正在改变人们的出行体验。从导航到娱乐、从安全到信息服务&#xff0c;车载应用的开发已成为汽车智能化发展的重要组成部分。而对于开发者来说&#xff0c;如何将自己的应用程序无缝集成到车载系统中&#xff0c;利用汽车的硬件和服务能力&#xff0c;是一个极具挑战性…

【Docker】01-Docker常见指令

1. Docker Docker会下载镜像&#xff0c;运行的时候&#xff0c;创建一个隔离的环境&#xff0c;称为容器。 docker run -d \ # 创建并运行一个容器&#xff0c;-d表示后台运行 --name mysql \ # 容器名称-p 3307:3306 \ # 端口映射&#xff0c;宿主机端口映射到容器端口-e TZ…

Cilium + ebpf 系列文章-什么是ebpf?(一)

前言&#xff1a; 这篇非常非常干&#xff0c;很有可能读不懂。 这里非常非常推荐&#xff0c;建议使用Cilium官网的lab来辅助学习&#xff01;&#xff01;&#xff01;Resources Library - IsovalentExplore Isovalents Resource Library, your one-stop destination for ins…

linux命令:显示已安装在linux内核的模块的详细信息的工具modinfo详解

目录 一、概述 二、使用方法 1、基本的使用语法 2、常用选项 3、输出字段 4、获取帮助 三、示例 四、实际用途 1、诊断问题 2、模块依赖 3、参数配置 五、其他事项 一、概述 modinfo 是 Linux 系统中的一个工具&#xff0c;用于显示有关已安装内核模块的详细信息。…

中间件:maxwell、canal

文章目录 1、底层原理&#xff1a;基于mysql的bin log日志实现的&#xff1a;把自己伪装成slave2、bin log 日志有三种模式&#xff1a;2.1、statement模式&#xff1a;2.2、row模式&#xff1a;2.3、mixed模式&#xff1a; 3、maxwell只支持 row 模式&#xff1a;4、maxwell介…

MySQL多版本并发控制MVCC实现原理

MVCC MVCC 是多版本并发控制方法&#xff0c;用来解决读和写之间的冲突&#xff0c;比如脏读、不可重复读问题&#xff0c;MVCC主要针对读操作做限制&#xff0c;保证每次读取到的数据都是本次读取之前的已经提交事务所修改的。 概述 当一个事务要对数据库中的数据进行selec…

十七,Spring Boot 整合 MyBatis 的详细步骤(两种方式)

十七&#xff0c;Spring Boot 整合 MyBatis 的详细步骤(两种方式) 文章目录 十七&#xff0c;Spring Boot 整合 MyBatis 的详细步骤(两种方式)1. Spring Boot 配置 MyBatis 的详细步骤2. 最后&#xff1a; MyBatis 的官方文档&#xff1a;https://mybatis.p2hp.com/ 关于 MyBa…

828华为云征文|使用Flexus X实例安装宝塔面板教学

目录 一、Flexus X实例简介 1.1 概述 1.2 产品规格 二、切换操作系统 2.1 Huawei Cloud EulerOS 2.0 标准版 2.2 切换镜像 三、部署宝塔面板 3.1 安装宝塔面板 3.2 放通安全组规则 3.3 登录宝塔面板 四、使用感受 4.1 柔性算力随心配 4.2 一直加速一直快 4.3 越用…

【小程序】微信小程序课程 -2 快速上手

目录 1、快速上手基本概念 1.1 小程序常用组件 1.2 tabbar配置 1.3 尺寸单位 1.4 样式 1.4.1 全局样式 app.wxss 1.4.2 局部样式 xx.wxss 2、首页案例 2.1 button组件使用 2.2 swiper swiper-item 2.3 tips效果 2.4 引入矢量图 2.5 flex&#xff08;布局&#…

Java中List、ArrayList与顺序表

List、ArrayList与顺序表 List什么是List常用方法介绍List的使用 ArrayList与顺序表线性表顺序表接口的实现 ArrayList简介ArrayList的使用ArrayList的构造ArrayList的常见操作ArrayList的遍历ArrayList的扩容机制 ArrayList的具体使用杨辉三角简单的洗牌算法 ArrayList的问题及…