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

news2024/12/25 15:26:17

请添加图片描述

文章目录

  • 📝基本框架
  • 🌠 构造和销毁
      • 🌉==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()

在这里插入图片描述

  1. 完全空值初始化:
vector()
	: _start(nullptr)
	, _finish(nullptr)
	, _end_of_storage(nullptr)
{}
  1. 缺省值初始化
	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 对象中。

这个拷贝构造函数的实现逻辑:

  1. 预留空间:首先调用 reserve() 函数为目标 vector 预留足够的空间来存储源 vector 的所有元素。这样可以避免在元素插入过程中多次分配内存,提高性能。

  2. 拷贝元素:通过范围基于 for 循环遍历源 vector 中的每个元素,并使用 push_back() 将这些元素逐个添加到目标 vector 中。
    push_backreserve()下文会有讲解。

🌉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 初始化为 0
    Example(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;

总结:
这种实现方式通过参数按值传递的副本,再通过交换数据来实现赋值运算,避免了临时资源分配失败导致的资源泄漏,并且非常高效,因为只交换指针,不进行实际数据复制。

  1. 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 是模板参数,表示输入迭代器的类型。由于这是一个模板函数,所以它可以接受任意类型的迭代器。
  • firstlast 是两个迭代器,定义了一个半开区间 [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::liststd::setstd::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> 类型的参数来初始化 vectorinitializer_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 的类型,这里 eT 类型。
    • 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;
}

插入位置检查: 确保插入位置 posvector 当前元素的范围内,即在有效的 [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 中迭代器失效的主要条件:

  1. 内存重新分配

vector 会在需要时扩展其内存容量。当 vector 的容量不足以容纳新元素时,它会重新分配内存。这通常发生在以下操作时:

  • 插入元素:当 vector 的容量满时,使用 push_backinsert 等方法插入新元素可能会触发内存重新分配。
  • 扩展容量:使用 reserve 增加 vector 的容量,如果 reserve 请求的容量大于当前容量,vector 可能会重新分配内存。

影响

  • 内存重新分配会导致所有原有的指针和迭代器失效,因为 vector 内部的元素被移动到新的内存位置。
  • 在内存重新分配后,原来的迭代器和指针将不再有效,因为它们指向的是旧的内存区域。
  1. 删除元素

删除元素通常不会导致内存重新分配,但会影响迭代器的有效性:

  • 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 = 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);
		}

		//类模版中也可以使用函数模版
		//函数模版 --- 目的支持任意容易得迭代器区间初始化
		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;

	}
}

🚩总结

请添加图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

XSS的DOM破坏

目录 1、DOM破坏案例解释 1.1先写一个demo.html文件 1.2、执行demo&#xff0c;看到是否将全部移除%20scr1%20οnerrοralert(1)> 分析&#xff1a; 解决&#xff1a;把两个for不在同一个数组进行操作 1.3、接下来我们要想办法让第二个for循环不能删除&#xff0c;留下…

android FD_SET_chk问题定位

android FD_SET_chk问题定位 一、FD报错二、问题定位2.1 APM定位2.2 adb定位2.3. 代码获取FD数 三、FD优化 一、FD报错 App在运行中记录报错如下&#xff0c;FD_SET&#xff0c;这个问题大概是文件描述符&#xff08;File Descriptor&#xff0c;简称FD&#xff09;超过了最大…

首款会员制区块链 Geist 介绍

今天&#xff0c;Pixelcraft Studios 很高兴地宣布即将推出 Geist&#xff0c;这是一个由 Base、Arbitrum、Alchemy 以及 Aavegotchi 支持的全新 L3。 Geist 之前的代号为 “Gotchichain”&#xff0c;是首个专为游戏打造的会员专用区块链。 为什么选择 Geist&#xff1f; …

Spring DI 简单演示三层架构——Setter 注入

Spring IOC 的常见注入方法有3种&#xff1a;Setter注入、构造注入和属性注入。想了解更多可点击链接&#xff1a;Spring 注入、注解以及相关内容补充 属性注入 不推荐。原因&#xff1a;使用私有的成员属性变量&#xff0c;依靠反射实现&#xff0c;破坏封装&#xff0c;只能依…

~Keepalived高可用集群~

一、Keepalived简介 是一个用于实现高可用性的解决方案&#xff0c;它主要应用于云主机的主备切换&#xff0c;以达到高可用性&#xff08;HA&#xff09;的目的。当主服务器发生故障无法对外提供服务时&#xff0c;动态将虚拟IP切换到备服务器&#xff0c;继续对外提供服务&a…

DOM破坏

XSS Game 1、第一关 Ma Spaghet! <!-- Challenge --> <h2 id"spaghet"></h2> <script>spaghet.innerHTML (new URL(location).searchParams.get(somebody) || "Somebody") " Toucha Ma Spaghet!" </script> S…

【ubuntu24.04】wget配置代理加速下载

参考之前的wget代理配置 wget速度非常慢 配置控制台代理不行 配置wget代理 本机部署了代理程序:all_proxy 不识别:root@PerfSvr:~# cat set65proxy.sh #!/bin/sh export

[STM32F429_硬件知识01]

知识点1 &#xff1a;J-Link的使用步骤&#xff1a; step1 : 安装J-Link驱动程序 step2 : keil的魔术棒中 -> Debug -> Use中选择J_Link ->点击 Settings ->

AI 时代风暴:程序员的核心竞争力大揭秘

引言&#xff1a; 在当今科技浪潮以排山倒海之势汹涌澎湃之际&#xff0c;人工智能宛如璀璨星辰般不断涌现&#xff0c;AIGC&#xff08;如 chatgpt、midjourney、claude 等&#xff09;大语言模型如雨后春笋般破土而出&#xff0c;AI 辅助编程工具更是以风驰电掣之速迅速席卷编…

智能电销机器人提升工作效率

随着科技的不断发展&#xff0c;电销行业也在不断探索创新&#xff0c;其中电销机器人作为一种高效的工具&#xff0c;正逐渐成为企业提升工作效率的利器。让我们一起看看电销机器人如何助力企业提高效率&#xff1a; 1. 自动化拨打电话 电销机器人每天可以自动拨打大量电话&a…

element-plus表格组件el-table 的使用

表格是在前端页面中是经常被用到的&#xff0c;尤其是管理系统&#xff0c;几乎每个页面都会存在表格&#xff0c;所以掌握表格组件是非常有必要的。element-plus提供el-table&#xff0c;el-table-column来渲染表格&#xff0c; 1. el-table 组件主要属性 属性名作用值类型…

【C语言小项目】五子棋游戏

目录 前言 一、游戏规则 1.功能分析 2.玩法分析 3.胜负判定条件 二、游戏实现思路 三、代码实现与函数封装 1.项目文件创建 2.头文件说明 3.函数封装 1&#xff09;菜单实现 2&#xff09;进度条实现 3&#xff09;main函数实现 4&#xff09;Game函数 5&#xff0…

Java语言程序设计——篇十三(4)

&#x1f33f;&#x1f33f;&#x1f33f;跟随博主脚步&#xff0c;从这里开始→博主主页&#x1f33f;&#x1f33f;&#x1f33f; 欢迎大家&#xff1a;这里是我的学习笔记、总结知识的地方&#xff0c;喜欢的话请三连&#xff0c;有问题可以私信&#x1f333;&#x1f333;&…

下载中心: 使用异步方法生成文件任务+键集分页查询

文章目录 引言I 下载中心功能进度表设计异步处理文件生成案例II 键集分页查询提高查询效率解决分页查询出现数据重复或丢失案例III 工具线程池基于EasyExcel 生成 excel文件存储系统see also引言 需求: 根据查询条件导出数据,比如交易流水、设备安装资料。 流程设计:点击导…

牛客面经学习笔记(二)

锂离子电池的充电过程可以分为四个阶段&#xff1a;涓流充电&#xff08;低压预充&#xff09;、恒流充电、恒压充电以及充电终止。 阶段1&#xff1a;涓流充电——涓流充电用来先对完全放电的电池单元进行预充(恢复性充电)。在电池电压低于3V左右时采用涓流充电&#xff0c;涓…

Vue 生命周期详解含demo、面试常问问题案例

Vue 生命周期详解、面试常问问题案例 含 demo 文章目录 Vue 生命周期详解、面试常问问题案例 含 demo一、Vue 生命周期是什么二、Vue 中如何使用生命周期钩子1. **beforeCreate**2. **created**3. **beforeMount**4. **mounted**5. **beforeUpdate**6. **updated**7. **beforeD…

8.15笔记

一、mycat读写分离实现 1. 添加一个新的虚拟主机&#xff0c;设置ip为10.1.1.60,主机名为mycat.yuanyu.zhangmin.关闭防火墙 SELinux NetworkManager 2. 上传jdk和mycat安装包 3. 解压并且添加到指定的位置 [rootmycat ~]# ls anaconda-ks.cfg frp initserver.sh jdk1.8…

018集——递归函数和for循环对比(从1加到100实例) ——C#学习笔记

本例分别用递归函数和for循环&#xff0c;求出1到100的和&#xff1a; using System;namespace Mytest {class Mytest{static void Main(string[] args){Calculate Myc new Calculate();int YourNumber 100;int Mysum Myc.Xto1(YourNumber);Console.WriteLine("从1加到…

Element-03.组件-Pagination分页

一.常见组件-分页-属性 参数&#xff1a;background 说明&#xff1a;是否为分页按钮添加背景色 类型&#xff1a;boolean 有background即添加&#xff0c;没有则不添加 参数&#xff1a;layout 说明&#xff1a;组件布局&#xff0c;子组件名用逗号分隔 类型&#x…

86.小米相机修改拍照(尺寸,画幅,比例)的方法

目录 1.打开相机&#xff0c;拍照模式&#xff0c;上面有个箭头或三个点&#xff0c;点击 2.点击画幅 3.点击你想要的画幅即可。 想要修改手机照片的&#xff08;尺寸&#xff0c;画幅&#xff0c;比例&#xff09;时&#xff0c;总会去找分辨率&#xff0c;其实并不是&…