C++奇迹之旅:手写vector模拟实现与你探索vector 容器的核心机制与使用技巧

文章目录
- 📝基本框架
- 🌠 构造和销毁
- 🌉==vector()==
- 🌉==vector(const vector& v)==
- 🌉==vector(size_t n, const T& value = T())==
- 🌉==赋值拷贝构造:vector<T>& operator=(vector<T> v)==
- 🌉==模版函数实现区间初始化 vector(InputIterator first, InputIterator last)==
- 🌉==函数模板的定义 InputIterator==
- 🌉==列表初始化区间vector(initializer_list<T> il)==
- 🌉~vector()
- 🌠begin与end
- 🌉reserve
- 函数实现
- 🌉resize
- 🌉insert与erase
- 🌉push_back与pop_back
- 🌠访问元素
- 🌠全代码
- 🌉test.cpp
- 🌉vector.h
- 🚩总结
📝基本框架
我们先定义自己的命名空间俩封装自定义的vector类,这样可以避免与标准库中的 vector 发生命名冲突。随即,我们定义模版类vector,三个成员变量都是迭代器,而vector迭代器又是原生指针,所以我们将指针取别名为iterator
框架代码:
namespace self
{template<class T>class vector{public:// 定义类型别名 iterator 和 const_iterator,用于表示指向元素的指针类型typedef T* iterator;typedef const T* const_iterator;private:private:iterator _start; // 指向动态数组的起始位置iterator _finish; // 指向当前数组中最后一个元素的下一个位置iterator _end_of_storage; // 指向已分配内存的末尾(容量的终点)};
}

理解代码并添加注释如下:
namespace self
{template<class T>class vector{public:// 定义迭代器类型typedef T* iterator;typedef const T* const_iterator;private:// 数据成员iterator _start; // 指向容器中第一个元素的指针iterator _finish; // 指向容器中最后一个元素之后的位置iterator _end_of_storage; // 指向容器所分配的存储空间的尾部// 在这里添加其他成员函数和成员变量};
}
成员变量的的私有数据成员:
iterator _start;: 这个成员变量保存了一个指向容器中第一个元素的指针。iterator _finish;: 这个成员变量保存了一个指向容器中最后一个元素之后的位置的指针。iterator _end_of_storage;: 这个成员变量保存了一个指向容器所分配的存储空间的尾部的指针。
🌠 构造和销毁
🌉vector()

- 完全空值初始化:
vector(): _start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
{}
- 缺省值初始化
vector() {};private:iterator _start = nullptr; // 指向动态数组的起始位置iterator _finish = nullptr; // 指向当前数组中最后一个元素的下一个位置iterator _end_of_storage = nullptr; // 指向已分配内存的末尾(容量的终点)
3.或者我们也可以偷个懒,让编译器自己生成
//强制编译器生成默认构造函数
vector() = default;
🌉vector(const vector& v)
这个构造函数是 vector 类的拷贝构造函数,用于创建一个新的 vector 对象,它是现有 vector 对象的拷贝。
vector(const vector<T>& v)
{reserve(v.size());for (auto e : v){push_back(e);}
}
这是一个拷贝构造函数,它接受一个 const 引用类型的 vector 对象 v 作为参数。这个函数的目的是创建一个新的 vector 对象,并将传入的 vector 对象 v 的内容拷贝到这个新的 vector 对象中。
这个拷贝构造函数的实现逻辑:
-
预留空间:首先调用
reserve()函数为目标vector预留足够的空间来存储源vector的所有元素。这样可以避免在元素插入过程中多次分配内存,提高性能。 -
拷贝元素:通过范围基于
for循环遍历源vector中的每个元素,并使用push_back()将这些元素逐个添加到目标vector中。
push_back和reserve()下文会有讲解。
🌉vector(size_t n, const T& value = T())
重载构造函数,用于创建一个 vector 对象,并将其初始化为 n 个指定值 value。
vector(size_t n, const T& value = T())
{for (size_t i = 0; i < n; ++i){push_back(value);}
}
-
这是一个带有两个参数的构造函数:
n:表示vector中元素的数量。value:表示每个元素的初始值,默认值为T(),即T类型的默认构造值。
-
这个构造函数用于创建一个
vector对象,并将其初始化为n个相同的元素,每个元素的值为value。 -
const T&表示一个对T类型的常量引用。使用常量引用可以避免在函数内部修改传入的值,并且通常比传值的方式更加高效,因为避免了不必要的复制操作。 -
value是参数的名字,它代表了要初始化vector中每个元素的值。 -
T()是T类型的默认构造函数的调用。它创建了一个T类型的对象,并使用默认构造函数来初始化它。 -
T()表示调用T类型的默认构造函数,生成一个T类型的临时对象。对于内置类型(如int,double),这通常是将其初始化为零;对于用户定义的类型(类或结构体),则会调用该类型的默认构造函数。
默认参数的作用:当构造函数被调用而未提供 value 参数时,value 会被初始化为 T(),即一个 T 类型的默认值。
- 如果提供了
value参数,那么构造函数会使用提供的值,而不是默认值。
假设 T 是一个简单的类或结构体:
class Example {
public:int data;Example() : data(0) {} // 默认构造函数,将 data 初始化为 0Example(int val) : data(val) {}
};
在 vector 的构造函数中:
vector(size_t n, const T& value = T())
{for (size_t i = 0; i < n; ++i){push_back(value);}
}
- 当你调用
vector(5),value会被初始化为T(),即Example(),因此所有元素将会使用Example的默认构造值(data为 0)。 - 当你调用
vector(5, Example(10)),value被初始化为Example(10),所有元素的data将会是 10。
总结:
T() 在 const T& value = T() 中的作用是提供一个默认值用于初始化 value 参数。这个默认值是通过调用 T 类型的默认构造函数得到的。这样,构造函数在没有提供具体值的情况下,也能正确地初始化 vector 对象中的元素。
🌉赋值拷贝构造:vector& operator=(vector v)
//v3 = v2
vector<T>& operator=(vector<T> v)
{swap(v);return *this;
}void swap(const vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);
}
赋值运算符重载 (operator=)
vector<T>& operator=(vector<T> v)
{swap(v);return *this;
}
这里分为三部:
1.1. 参数 vector<T> v
- 这个赋值运算符重载接受一个
vector<T>对象v作为参数。这里的v是按值传递的,这意味着传递的是v的一个副本。 - 传递副本而不是引用,有助于实现 强异常安全保证。如果在赋值过程中出现异常,原来的
vector不会被影响。
1.2. swap(v)
swap(v)是核心操作,它交换当前对象 (*this) 与传入的v对象的数据成员。- 这个操作通过交换指针来交换两个
vector对象的内部数据(如起始位置_start、结束位置_finish、存储容量边界_end_of_storage),而无需进行逐个元素的复制。 - 交换后,传入的副本
v原来持有的资源会被销毁,而当前对象将接管这些资源。
1.3. return *this
- 最后返回当前对象的引用
*this,允许链式赋值操作,如v1 = v2 = v3;。
总结:
这种实现方式通过参数按值传递的副本,再通过交换数据来实现赋值运算,避免了临时资源分配失败导致的资源泄漏,并且非常高效,因为只交换指针,不进行实际数据复制。
swap函数
void swap(const vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);
}
const vector<T>& v这里是引用:
swap 函数接受一个 const vector<T>& v 作为参数。因为这个函数只是交换内部指针,v 被声明为 const 是为了表明不会修改 v 的逻辑内容(但实际上会修改 v 的内部指针)。 std::swap 是标准库中的模板函数,交换两个变量的值。这里通过交换 _start、_finish 和 _end_of_storage 三个指针,实现了两个 vector 对象之间的数据交换。
函数效果:交换完成后,原对象和传入的对象交换了内部数据指针,但没有改变实际存储的数据。这种方式避免了内存分配和数据复制操作的开销,提高了效率。
🌉模版函数实现区间初始化 vector(InputIterator first, InputIterator last)
这个构造函数是一个模板函数,目的是让 vector 类支持任意类型的迭代器区间初始化。通过这个构造函数,你可以使用两个迭代器来初始化一个 vector 对象,将迭代器区间 [first, last) 中的所有元素插入到 vector 中。让我们逐步分析这段代码:
//类模版中也可以使用函数模版
//函数模版 --- 目的支持任意容易得迭代器区间初始化
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);++first;}
}
🌉函数模板的定义 InputIterator
template<class InputIterator>
vector(InputIterator first, InputIterator last)
- 这是一个函数模板,用于在
vector类中定义一个构造函数。InputIterator是模板参数,表示输入迭代器的类型。由于这是一个模板函数,所以它可以接受任意类型的迭代器。 first和last是两个迭代器,定义了一个半开区间[first, last)。这个区间的所有元素将被复制到新的vector中。
循环遍历迭代器区间
while (first != last)
{push_back(*first);++first;
}
while (first != last):这个 while 循环用于遍历迭代器区间 [first, last)。只要 first 不等于 last,就会继续循环。
push_back(*first):
*first是解引用操作,获取当前first所指向的元素值。push_back(*first)将这个值添加到vector的末尾。push_back()函数会处理必要的内存扩展,因此即使vector当前容量不足,它也能动态扩展存储空间,以容纳新元素。
函数的功能和用途:
-
功能:这个模板构造函数允许你用任意的迭代器区间来初始化一个
vector。这个区间可以是数组、std::list、std::set、std::deque等容器的迭代器区间,甚至是原始指针。 -
用途:这种灵活性使得
vector可以从几乎任何标准容器或数组中初始化。例如,如果你有一个数组int arr[] = {1, 2, 3, 4};,可以使用这个构造函数将arr的内容复制到vector中:vector<int> vec(arr, arr + 4);
🌉列表初始化区间vector(initializer_list il)
vector(initializer_list<T> il)
{reserve(il.size());for (auto e : il){push_back(e);}}
这段代码是 vector 类的一个构造函数,它接受一个 initializer_list<T> 类型的参数来初始化 vector。initializer_list 是 C++11 引入的一个特性,允许以列表的形式直接初始化容器。下面详细分析这段代码的每一部分:
vector(initializer_list<T> il)
initializer_list<T>是 C++11 中引入的标准库类型,表示一个初始化列表,它是一个常量迭代器区间,支持对T类型的元素进行初始化。il是构造函数的参数,代表一个初始化列表,其中包含了要插入到vector中的元素。
reserve(il.size());
reserve()是vector类的一个成员函数,用于预先分配一定的内存空间,以避免在添加元素时频繁重新分配内存。il.size()返回初始化列表中元素的数量。调用reserve(il.size())可以确保vector在添加所有元素之前有足够的空间,从而提高性能。
for (auto e : il)
{push_back(e);
}
-
范围基于
for循环 (for (auto e : il)) 遍历初始化列表il中的每一个元素。auto关键字让编译器自动推断e的类型,这里e是T类型。il是一个initializer_list<T>,它提供了迭代器接口,因此可以用这种方式进行遍历。
-
push_back(e)将当前元素e插入到vector的末尾。push_back()方法会将元素e添加到vector中。如果vector的当前容量不足以容纳新的元素,它会自动扩展容量。
优点和使用场景
- 易用性:支持直接使用花括号初始化列表,例如:
这种方式非常直观,易于理解和使用。self::vector<int> v = {1, 2, 3, 4, 5};
示例
假设我们有一个 vector<int>,我们可以使用这个构造函数初始化 vector:
self::vector<int> v = {1, 2, 3, 4, 5};
- 这个语句会创建一个
vector<int>对象v,并将初始化列表{1, 2, 3, 4, 5}中的元素依次插入到vector中。
🌉~vector()
析构函数,我们只需要释放空间并置指针指向空:
~vector()
{if (_start){delete[] _start;_start = _finish = _end_of_storage = nullptr; }
}
🌠begin与end
迭代器开始与结束:
iterator begin()
{return _start;
}iterator end()
{return _finish;
}iterator begin() const
{return _start;
}iterator end() const
{return _finish;
}const_iterator cbegin() const
{return _start;
}const_iterator cend() const
{return _finish;
}
获取容量大小与有效元素个数,只需要进行对应指针相减
size_t size() const
{return _finish - _start;
}size_t capacity() const
{return _end_of_storage - _start;
}
🌉reserve
void reserve(size_t n)
{if (n > capacity()){size_t old_size = size();//记录原size()的大小T* tmp = new T[n];if (_start){memcpy(tmp, _start, sizeof(T) * size());delete[] _start;}_start = tmp;_finish = _start + old_size;//避免使用原来finish指针与更新后的start指针相减得到错误的size()_end_of_storage = _start + n;}}

这个 reserve 函数用于在 vector 类中预留一定的内存,以便容纳 n 个元素。这个方法的主要目的是扩展 vector 的容量,确保它能够容纳更多的元素,同时管理内部内存的重新分配。下面详细分析这段代码:
函数实现
void reserve(size_t n)
{if (n > capacity()){size_t old_size = size(); // 记录原 size() 的大小T* tmp = new T[n]; // 分配新的内存空间if (_start){memcpy(tmp, _start, sizeof(T) * size()); // 复制原有元素delete[] _start; // 释放原内存}_start = tmp; // 更新 start 指针_finish = _start + old_size; // 更新 finish 指针_end_of_storage = _start + n; // 更新 end_of_storage 指针}
}
记录原大小
size_t old_size = size();
size()返回vector中当前元素的数量。old_size用于记录当前vector中的元素数量,以便在内存扩展后,能够正确地设置_finish指针。
因为不记录,等delete了_start,到这两部
_start = tmp; // 更新 start 指针,_finish = _start + size();再用旧的减去一个释放的_start,就会有问题。
这里我们执行深拷贝,通过memcpy将旧数组的元素复制到新数组。需要注意的是,memcpy是基于字节的浅拷贝,如果vector实例化为string类,浅拷贝可能导致二次释放等问题。

虽然我的_start指向新空间并进行了深拷贝,但string类却进行了浅拷贝,仍然指向原始空间。为了解决这个问题,我们不能使用memcpy,而是需要通过赋值操作进行二次深拷贝。
void reserve(size_t n)
{if (n > capacity()){T* tmp = new T[n];size_t old_size = size();//memcpy(tmp, _start, size() * sizeof(T));for (size_t i = 0; i < old_size; i++){tmp[i] = _start[i];}delete[] _start;_start = tmp;_finish = tmp + old_size;_endofstorage = tmp + n;}
}
🌉resize
void resize(size_t n, const T& x = T())
{if (n < size()) // 如果新的大小小于当前大小{_finish = _start + n; // 将_finish指针移动到新大小的位置}else if (n > size()) // 如果新的大小大于当前大小{size_t oldSize = size();reserve(n); // 确保有足够的容量来存储新大小的元素for (size_t i = oldSize; i < n; ++i){push_back(x); // 用默认值x填充新的元素}}// 如果 n 等于当前大小,不需要进行任何操作
}
如果新大小小于当前大小,则截断容器中多余的元素;如果新大小大于当前大小,则使用给定的填充值来填充新的元素。
🌉insert与erase

iterator insert(iterator pos, const T& x)
{assert(pos >= _start);assert(pos <= _finish);if (_finish == _end_of_storage){size_t len = pos - _start;size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;
}
插入位置检查: 确保插入位置 pos 在 vector 当前元素的范围内,即在有效的 [start, finish] 区间内。
内存扩展
if (_finish == _end_of_storage)
{size_t len = pos - _start;size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);pos = _start + len;
}
- 如果
vector当前容量已满(_finish == _end_of_storage),则需要扩展容量。 - 计算插入位置在原始
vector中的索引len,并使用reserve(newcapacity)扩展容量。 - 扩展容量后,更新
pos的位置,使其指向新的内存区域中插入的目标位置。这里pos = _start + len是为了确保扩展后的pos位置正确。
这里的len记录原来位置是防止迭代器失效问题:因为我们使用了reserve,开了新空间,如果未保存,使用新的地址,那将会有风险。
返回值
return pos;
- 返回新插入元素的位置
pos,使得插入操作可以链式使用,更新迭代器。
在 C++ 中,std::vector 是一个动态数组,它会根据需要扩展其内部存储的容量。由于这种扩展和其他操作,vector 中的迭代器可能会失效。以下是 vector 中迭代器失效的主要条件:
- 内存重新分配
vector 会在需要时扩展其内存容量。当 vector 的容量不足以容纳新元素时,它会重新分配内存。这通常发生在以下操作时:
- 插入元素:当
vector的容量满时,使用push_back、insert等方法插入新元素可能会触发内存重新分配。 - 扩展容量:使用
reserve增加vector的容量,如果reserve请求的容量大于当前容量,vector可能会重新分配内存。
影响:
- 内存重新分配会导致所有原有的指针和迭代器失效,因为
vector内部的元素被移动到新的内存位置。 - 在内存重新分配后,原来的迭代器和指针将不再有效,因为它们指向的是旧的内存区域。
- 删除元素
删除元素通常不会导致内存重新分配,但会影响迭代器的有效性:
erase:调用erase方法删除元素会使指向被删除元素的迭代器失效。- 范围删除:
erase删除一段范围内的元素时,范围内的所有迭代器会失效,此外,所有指向被删除元素之后的迭代器也会失效,因为元素的后续位置发生了改变。
影响:
erase操作可能导致指向被删除元素的迭代器失效。- 被删除元素之后的所有迭代器也会失效,因为删除操作可能会导致元素的重新排列。
在对 vector 进行赋值或移动操作时,虽然这些操作不会直接影响单个迭代器,但会对迭代器的使用产生影响:
- 赋值操作:将一个
vector赋值给另一个vector,会涉及到内存重新分配和元素复制,这可能会使原有的迭代器失效。 - 移动操作:使用移动构造函数或移动赋值操作时,也可能导致内部数据的重新分配和元素的重新排列,从而使迭代器失效。
为了避免迭代器失效的影响,在进行可能导致失效的操作后,应当重新获取迭代器或使用容器提供的稳定操作。例如,可以使用 vector 提供的 begin() 和 end() 重新获取迭代器。在设计使用迭代器的代码时,应考虑这些因素,以确保代码的正确性和可靠性。

iterator erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);iterator end = pos + 1;while (end <= _finish){*(end - 1) = *end;++end;}--_finish;return pos;
}
迭代器失效 :删除操作会使指向被删除元素的迭代器失效。删除一个元素,迭代器还指向原位置,但元素被移动了,也就是原位置的后一个元素来到原位置,因此注意 erase 后,pos 之后的迭代器要更新指向新位置。
🌉push_back与pop_back
push_back函数可以复用,也可以使用insert的方法构造:
void push_back(const T& x)
{//if (_finish == _end_of_storage)//{// size_t newcapacity = capacity() == 0 ? 4 : sizeof(T) * 2;// reserve(newcapacity);//}//*_finish = x;//_finish++;insert(end(), x);
}
void pop_back()
{assert(size() > 0);--_finish;}
🌠访问元素
访问元素operator[]需要注意的就是,要判断是否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];
}
🌠全代码
代码中有丰富的测试用例来验证vector的实现是否有问题:
🌉test.cpp
# define _CRT_SECURE_NO_WARNINGS 1
#include "vector.h"int main()
{//self::test_vector01();//self::test_vector02();//self::test_vector03();//self::test_vector04();//self::test_vector05();//self::test_vector06();//self::test_vector07();self::test_vector08();return 0;
}
🌉vector.h
#pragma once
#include <iostream>
#include <list>
#include <assert.h>
using namespace std;namespace self
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _start;}iterator end(){return _finish;}iterator begin() const{return _start;}iterator end() const{return _finish;}const_iterator cbegin() const{return _start;}const_iterator cend() const{return _finish;}//强制编译器生成默认构造函数vector() = default;//s2(s1)vector(const vector<T>& v){reserve(v.size());for (auto e : v){push_back(e);}}vector(size_t n, const T& value = T()){for (size_t i = 0; i < n; ++i){push_back(value);}}//v3 = v2vector<T>& operator=(vector<T> v){swap(v);return *this;}void swap(const vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);}//类模版中也可以使用函数模版//函数模版 --- 目的支持任意容易得迭代器区间初始化template<class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);++first;}}vector(initializer_list<T> il){reserve(il.size());for (auto e : il){push_back(e);}}~vector(){if (_start){delete[] _start;_start = _finish = _end_of_storage = nullptr; }}size_t size(){return _finish - _start;}size_t capacity(){return _end_of_storage - _start;}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 old_size = size();//记录原size()的大小T* tmp = new T[n];//if (_start)//{// memcpy(tmp, _start, sizeof(T) * size());// delete[] _start;//}for (size_t i = 0; i < old_size; i++){tmp[i] = _start[i];}delete[] _start;_start = tmp;_finish = _start + old_size;//避免使用原来finish指针与更新后的start指针相减得到错误的size()_end_of_storage = _start + n;}}void resize(size_t n, const T& x = T()){if (n < size()) // 如果新的大小小于当前大小{_finish = _start + n; // 将_finish指针移动到新大小的位置}else if (n > size()) // 如果新的大小大于当前大小{size_t oldSize = size();reserve(n); // 确保有足够的容量来存储新大小的元素for (size_t i = oldSize; i < n; ++i){push_back(x); // 用默认值x填充新的元素}}// 如果 n 等于当前大小,不需要进行任何操作}void push_back(const T& x){//if (_finish == _end_of_storage)//{// size_t newcapacity = capacity() == 0 ? 4 : sizeof(T) * 2;// reserve(newcapacity);//}//*_finish = x;//_finish++;insert(end(), x);}void pop_back(){assert(size() > 0);--_finish;}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);assert(pos <= _finish);if (_finish == _end_of_storage){size_t len = pos - _start;size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);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);assert(pos < _finish);iterator end = pos + 1;while (end <= _finish){*(end - 1) = *end;++end;}--_finish;return pos;}private:iterator _start;iterator _finish;iterator _end_of_storage;};void test_vector01(){self::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(4);for (size_t i = 0; i < v1.size(); i++){cout << v1[i];}cout << endl;self::vector<int>::iterator it = v1.begin();while (it != v1.end()){cout << *it << " ";it++;}cout << endl;for (auto e : v1){cout << e << " ";}cout << endl;v1.pop_back();for (auto e : v1){cout << e << " ";}cout << endl;}void test_vector02(){self::vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);for (auto e : v1){cout << e << " ";}cout << endl;size_t x = 0;cin >> x;self::vector<int>::iterator it = find(v1.begin(), v1.end(), x);if (it != v1.begin()){it = v1.insert(it, 100);cout << *it << endl;}for (auto e : v1){cout << e << " ";}cout << endl;}void test_vector03(){self::vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);for (auto e : v1){cout << e << " ";}cout << endl;size_t x = 0;cin >> x;self::vector<int>::iterator it = find(v1.begin(), v1.end(), x);if (it != v1.end()){it = v1.erase(it);if (it != v1.end())cout << *it << endl;}for (auto e : v1){cout << e << " ";}cout << endl;cout << typeid(it).name() << endl;}void test_vector04(){vector<int> v3;v3.push_back(1);v3.push_back(2);v3.push_back(3);v3.push_back(4);vector<int>::iterator it = v3.begin();while (it != v3.end()){if (*it % 2 == 0){v3.erase(it);}else{++it;}}for (auto e : v3){cout << e << " ";}cout << endl;}void test_vector05(){vector<int> v6;v6.push_back(1);v6.push_back(1);v6.push_back(1);for (auto e : v6){cout << e << " ";}cout << endl;vector<int> v7(v6);for (auto e : v7){cout << e << " ";}cout << endl;vector<int> v8 = v6;for (auto e : v8){cout << e << " ";}cout << endl;}void test_vector06(){int i = 0;int j(1);int k = int();int x = int(2);vector<string> v8(4, "xxxxx");for (auto e : v8){cout << e << " ";}cout << endl;vector<int> v9(4u, 104);for (auto e : v9){cout << e << " ";}cout << endl;vector<int> v10(v9.begin(), v9.end());for (auto e : v9){cout << e << " ";}cout << endl;}void test_vector07(){vector<string> v8(4, "xxxxx");for (auto e : v8){cout << e << " ";}cout << endl;list<int> It;It.push_back(100);It.push_back(100);It.push_back(100);It.push_back(100);list<int> v3(It.begin(), It.end());for (auto e : v3){cout << e << " ";}cout << endl;}class A{public:A(int a = 0):_a1(a), _a2(0){}A(int a1, int a2):_a1(a1), _a2(a2){}private:int _a1;int _a2;};void test_vector08(){A aa1(1);A aa2(2, 2);const A& aa3 = { 1, 2 };A aa4 = 1;A aa5 = { 1,3 };vector<int> v1({ 1,2,3,4,5,6 });vector<int> v2 = { 1,2,3,4,5,8 };for (auto e : v1){cout << e << " ";}cout << endl;for (auto e : v2){cout << e << " ";}cout << endl;auto il1 = { 1,2,3,4 };initializer_list<int> il2 = { 1,3,4 };cout << typeid(il1).name() << endl;cout << sizeof(il2) << endl;for (auto e : il2){cout << e << " ";}cout << endl;}
}
🚩总结

相关文章:
C++奇迹之旅:手写vector模拟实现与你探索vector 容器的核心机制与使用技巧
文章目录 📝基本框架🌠 构造和销毁🌉vector()🌉vector(const vector& v)🌉vector(size_t n, const T& value T())🌉赋值拷贝构造:vector<T>& operator(vector<T> v)&a…...
018、钩子函数 mounted和beforeDestroy、父组件向子组件传递参数 props 的使用
文章目录 1、mounted 和 beforeDestroy1.1、mounted1.2、beforeDestroy 2、父组件向子组件传递参数 props2.1、子组件定义2.2、父组件调用子组件并传参 3、完整例子3.1、父组件 Tags.vue3.2、子组件 TagsMenu.vue3.3、效果图 1、mounted 和 beforeDestroy 1.1、mounted mount…...
xlnt在Windows中的dll,lib生成
前言 花了半天时间想要把xlnt 集成到VS2022 Cmake项目中,以我目前掌握的能力,Cmake语法对于我来说难懂,对于只是使用过Cmake编译MySQL,或是其他lib,dll库的小白来说,不应该为了显示自己能力多么出众,强行去配置一些程序内容。 生活中没有绝对的事情,有舍有得. https://github…...
【网络】私有IP和公网IP的转换——NAT技术
目录 引言 NAT工作机制编辑 NAT技术的优缺点 优点 缺点 个人主页:东洛的克莱斯韦克-CSDN博客 引言 公网被子网掩码划分为层状结构,一个公网IP的机器又可以用很多私有IP搭建内网。在日常生活场景中用的都是私有IP,例如手机,…...
java 面试 PDF 资料整理
“尊贵的求知者,作者特此献上精心编纂的Java面试宝典PDF,这份资料凝聚了无数面试精华与实战经验,是通往Java技术殿堂的钥匙。若您渴望在Java编程的求职之路上稳健前行,只需轻轻一点,完成这象征支持与认可的一键三联&am…...
初步认识Linux系统
前言 Linux系统具有许多优点,不仅系统性能稳定,而且是开源软件。其核心防火墙组件性能高效、配置简单,保证了系统的安全。在很多企业网络中,为了追求速度和安全,Linux不仅仅是被网络运维人员当作服务器使用,…...
JavaScript AI 编程助手
JavaScript AI 编程助手 引言 随着人工智能技术的飞速发展,编程领域也迎来了前所未有的变革。JavaScript,作为全球最流行的编程语言之一,其与AI的结合为开发者带来了巨大的便利和无限的可能性。本文将探讨JavaScript AI编程助手的定义、功能…...
达梦数据库的系统视图v$datafile
达梦数据库的系统视图v$datafile 达梦数据库的V$DATAFILE 是一个重要的系统视图,提供了有关数据库数据文件的信息。 V$DATAFILE 系统视图 V$DATAFILE 视图用于显示数据库中每一个数据文件的详细信息。通过查询这个视图,数据库管理员可以了解数据文件的…...
Triton/window安装: triton-2.0.0-cp310-cp310-win_amd64.whl文件
下面这个github仓: https://github.com/PrashantSaikia/Triton-for-Windows/tree/main 安装命令也很简单,下载到本地后运行: pip install triton-2.0.0-cp310-cp310-win_amd64.whl...
应急响应-DDOS-典型案例
某单位遭受DDoS攻击事件如下 事件背景 2019年2月17日,某机构门户网站无法访问,网络运维人员称疑似遭受DDoS攻击,请求应急响应工程师协助。 事件处置 应急响应工程师在达到现场后,通过查看流量设备,发现攻击者使用僵…...
JAVA学习之知识补充(下)
六:File类与IO流: 这里给出三种常见的初始化方法: 通过文件路径初始化: File file new File("C:/example/test.txt");这种方法用于创建一个文件对象,该文件对象表示指定路径的文件或目录。例如:File fil…...
qt生成一幅纯马赛克图像
由于项目需要,需生成一幅纯马赛克的图像作为背景,经过多次测试成功,记录下来。 方法一:未优化方法 1、代码: #include <QImage> #include <QDebug> #include <QElapsedTimer>QImage generateMosa…...
python循环——九九乘法表(更加轻松的理解循环结构)
感受 首先,得明确意识到这个问题,就是我的循环结构学的一塌糊涂,完全不能很好的使用这个循环来实现各种九九乘法表达输出,这样的循环结构太差了,还需要我自己找时间来补充一下循环的使用,来拓宽自己的思考方…...
UDS诊断系列之十八故障码的状态掩码
在谈19服务的子功能之前,先说一下故障码(DTC)的状态掩码是什么。 一、状态掩码 状态掩码由八个状态位构成,客户端利用它向服务器请求与其状态相匹配的DTC信息。当服务器接收到来自客户端的请求时,它会通过过滤匹配的…...
【jvm】直接引用
目录 1. 说明2. 形式3. 特点4. 生成过程5. 作用 1. 说明 1.在Java虚拟机(JVM)中,直接引用(Direct Reference)是相对于符号引用(Symbolic Reference)而言的,它是指向内存中实际存在的…...
PythonStudio 控件使用常用方式(二十七)TActionList
PythonStudio是一个极强的开发Python的IDE工具,官网地址是:https://glsite.com/ ,在官网可以下载最新版的PythonStudio,同时,在使用PythonStudio时,它也能及时为用户升到最新版本。它使用的是Delphi的控件&…...
PDF 转Word 开源库
1. Apache PDFBox Apache PDFBox 是一个开源的 Java 库,用于创建和操作 PDF 文档。虽然 PDFBox 本身没有直接支持 PDF 转 Word 的功能,但它可以提取 PDF 内容,你可以结合其他方法将这些内容写入 Word。 添加依赖 <dependency><gr…...
Docker - 深入理解Dockerfile中的 RUN, CMD 和 ENTRYPOINT
RUN docker file 中的 RUN 命令相对来教容易理解 RUN 指令用于在构建镜像时执行命令,这些命令会在 Docker 镜像的构建过程中执行。常用于安装软件包、设置环境变量、创建目录等。RUN 指令会在镜像构建中创建新的镜像层,每个 RUN 指令都会创建一个新的镜…...
Python 函数式编程 内置高阶函数及周边【进阶篇 3】推荐
前面我们已经总结并实践了用python获取到了数据。也介绍了python中http网络请求的几种方式,正在学习python开发语言或者对python3知识点生疏需要回顾的请点这里 ,本章主要总结了函数式编程及特点 和 python中内置的高阶函数及周边知识,方便自…...
【Rust光年纪】探秘Rust GUI库:从安装配置到API概览
Rust语言GUI库全方位比较:选择适合你的工具 前言 在现代软件开发中,图形用户界面(GUI)库扮演着至关重要的角色。随着Rust语言的不断发展,越来越多的优秀的GUI库也相继问世,为Rust开发者提供了更多选择。本…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...
从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...
Python训练营-Day26-函数专题1:函数定义与参数
题目1:计算圆的面积 任务: 编写一个名为 calculate_circle_area 的函数,该函数接收圆的半径 radius 作为参数,并返回圆的面积。圆的面积 π * radius (可以使用 math.pi 作为 π 的值)要求:函数接收一个位置参数 radi…...
