📝基本框架
我们先定义自己的命名空间俩封装自定义的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 初始化为 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;
。
总结:
这种实现方式通过参数按值传递的副本,再通过交换数据来实现赋值运算,避免了临时资源分配失败导致的资源泄漏,并且非常高效,因为只交换指针,不进行实际数据复制。
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 = 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;
}
}