1. ベクターの実装をシミュレートする
1.1 コンストラクター
デストラクター
C++ では、ベクトルは、必要に応じて自動的にサイズ変更される動的配列コンテナーです。ベクター クラスは、ベクター オブジェクトを作成および初期化するためのいくつかの異なるコンストラクターを提供します。
(1) デフォルトのコンストラクター:vector<type> v;空のベクター オブジェクト v を作成します (type は Vector 内の要素のデータ型です)。このコンストラクターは、初期サイズ 0 のベクトルを作成します。
(2) 初期サイズのコンストラクター:vector<type> v(n);サイズ n のベクトル オブジェクト v を作成し、すべての要素をデフォルト値に初期化します。たとえば、型が int の場合、すべての要素は 0 に初期化されます。
(3) 初期値を持つコンストラクタ:vector<type> v(n, 初期値);サイズ n のベクトル オブジェクト v を作成し、すべての要素を指定された初期値に初期化します。たとえば、型が int で初期値が 5 の場合、すべての要素は 5 に初期化されます。
(4) コピーコンストラクター:vector<type> v(v2);別のベクトル オブジェクト v2 と同じ要素を含む新しいベクトル オブジェクト v を作成します。このコンストラクターは、v2 のすべての要素をコピーします。
(5) イテレータのコンストラクタを使用します: Vector<type> v(begin, end);イテレータの begin から iterator end までのすべての要素を含む新しいベクトル オブジェクト v を作成します。このコンストラクターは要素をコピーします。
namespace vec
{
//类模板
template<class T>
class vector
{
public:
//带有初始值的构造函数
vector(size_t n, const T& val = T())
{
resize(n, val);
}
//带有初始值的构造函数
vector(int n, const T& val = T())
{
resize(n, val);
}
//使用迭代器的构造函数
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{
while (first != last)
{
push_back(*first);
++first;
}
}
//空构造函数
vector()
{
}
构造函数
//vector()
// :_start(nullptr)
// ,_finish(nullptr)
// ,_end_of_storage(nullptr)
//{}
//深拷贝实现拷贝构造
vector(const vector<T>& v)
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
_start = new T[v.capacity()];
//memcpy(_start, v._start, sizeof(T) * v.size());
for (size_t i = 0; i < v.size(); i++)
{
_start[i] = v._start[i];
}
_finish = _start + v.size();
_end_of_storage = _start + v.capacity();
}
//析构函数
~vector()
{
if (_start != nullptr)
{
delete[] _start;
_start = nullptr;
_finish = nullptr;
_end_of_storage = nullptr;
}
}
private:
//成员变量给缺省值
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _end_of_storage = nullptr;
};
}
1.2 イテレータ
文字列反復子の使用と同様に、begin 関数は文字列の先頭を指す反復子を返し、end 関数は文字列の末尾を指す反復子を返します。このようにして、ループを使用して各文字を反復処理するなど、標準の反復子操作を使用して文字列を走査できます。
通常のベクトル型の場合、コンパイラは上記の 2 種類の反復子の反復子を呼び出し、この型のコンテナが読み書きできることを示します。const 型のベクトルの場合、コンパイラは次の 2 種類の const_iterators の反復子を呼び出します。このタイプのコンテナに対する書き込み権限のみを示します。
このように、begin() 関数と end() 関数を呼び出すことで、ベクター コンテナーの開始位置と終了位置のイテレーターを取得し、そのイテレーターを使用してコンテナー内の要素を走査して操作できます。
//T类指针来实现迭代器
typedef T* iterator;
typedef const T* const_iterator;
//迭代器
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
//const修饰的迭代器
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
1.3 演算子のオーバーロード
代入演算子のオーバーロード
代入演算子 Operator= は、ベクトル オブジェクトを引数として受け入れ、現在のオブジェクトへの参照を返すようにオーバーロードされます。オーバーロードの実装では、まず一時的なベクトル オブジェクト v を作成し、受信したベクトル オブジェクトを v にコピーします。次に、swap 関数を呼び出して、現在のオブジェクトと一時オブジェクトのメンバー変数を交換します。最後に、継続的な代入操作をサポートするために、現在のオブジェクトへの参照が返されます。
このようにして、効率的な代入操作が実現され、要素ごとのコピーが回避され、パフォーマンスが向上します。同時に、スワップ機能を使用して交換することで、例外が発生したときにリソースのリークが発生しないようにすることができます。
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(v);
return *this;
}
演算子[]のオーバーロード
通常のバージョンでは、operator[] 演算子をオーバーロードし、読み取りおよび書き込み可能な参照を返します。アクセスする要素の位置を示す 1 つのパラメーター pos を受け入れます。関数内で、assert 関数を使用してアサートし、pos が有効な範囲内にあること、つまりベクトルのサイズより小さいことを確認します。次に、_start[pos] への参照を返し、指定された位置の要素の読み取りと書き込みを行います。
const 型のベクトルの場合、以前のバージョンの Operator[] 演算子に const を追加して、読み取り専用の参照を変更して返します。また、パラメータ pos を受け入れ、assert 関数を通じてアサートして、pos が有効範囲内にあることを確認します。次に、指定された位置にある要素への読み取り専用アクセスのために、_start[pos] への const 参照を返します。
Operator[] 演算子をオーバーロードすると、配列を使用するのと同じように添字を介してベクター コンテナ内の要素にアクセスできます。また、operator[] の定数バージョンをオーバーロードすることにより、定数オブジェクトに対する読み取り専用アクセスも可能になります。
//重载operator[] 可读可写
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
//重载operator[] 只读不可写
const T& operator[](size_t pos) const
{
assert(pos < size());
return _start[pos];
}
1.4 拡張機能
予約とサイズ変更
予約関数は、ベクトル オブジェクトのサイズを少なくとも n になるようにするために使用されます。nが現在の容量より大きい場合は、容量拡張が必要となります。関数内では、まず現在のベクトルのサイズを取得し、次にサイズ n の新しい配列を指す一時ポインター tmp を作成します。現在のベクトルが空でない場合は、元の要素を新しい配列に 1 つずつコピーします。
要素タイプがカスタム タイプの場合、シャロー コピーの問題が発生する可能性があり、memcpy 関数をメモリ コピーに使用できないことに注意してください。
サイズ変更関数は、ベクトル オブジェクトのサイズを n に調整し、必要に応じて指定された要素値 val を埋めるために使用されます。n が現在のサイズより小さい場合は、_finish ポインタを新しい位置に移動します。つまり、ベクトルを切り詰めます。n が現在のサイズより大きい場合は、reserve 関数を呼び出して容量拡張を実行します。次に、_finish ポインターが新しい位置を指すまでループすることにより、新しい要素の値がベクターにコピーされます。これは、必要に応じてベクトルを n にサイズ変更し、指定された要素の値を埋める機能を実装します。
これら 2 つの機能により、ベクター コンテナーの容量とサイズを調整したり、展開やサイズ変更の際に要素をコピーしたり埋めたりすることができます。このようにして、ベクターコンテナのメモリ空間と要素数を柔軟に管理することができます。
//扩容函数capacity
void reserve(size_t n)
{
if (n > capacity())
{
size_t sz = size();
T* tmp = new T[n];
if (_start)
{
//如果是自定义类型,假如string,则会出现浅拷贝
//memcpy(tmp, _start, sizeof(T) * sz);
for (size_t i = 0; i < sz; i++)
{
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = _start + sz;
_end_of_storage = _start + n;
}
}
//扩容size
void resize(size_t n, const T& val = T())
{
if (n < size())
{
_finish = _start + n;
}
else
{
reserve(n);
while (_finish != _start + n)
{
*_finish = val;
++_finish;
}
}
}
容量とサイズ
容量のサイズ範囲は次のとおりです: _end_of_storage - _start
size のサイズ範囲は次のとおりです: _finish - _start
ベクトルのサイズは必ずしも容量と等しい必要はなく、サイズが容量以下になる可能性があることに注意してください。ベクトルに要素を追加するとサイズは増加し、ベクトルから要素を削除するとサイズは減少します。サイズが容量を超えると、ベクターは自動的にメモリを再割り当てし、通常はより大きなメモリ空間を割り当て、元の要素を新しいメモリ空間にコピーし、元のメモリ空間を解放します。
//返回capacity
size_t capacity() const
{
return _end_of_storage - _start;
}
//返回size
size_t size() const
{
return _finish - _start;
}
1.5 追加、削除、確認、変更
プッシュバック
Push_back 関数の実装。ベクトルの末尾に要素を追加するために使用されます。
//尾插
void push_back(const T& v)
{
if (_finish == _end_of_storage)
{
size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
reserve(newcapacity);
}
*_finish = v;
++_finish;
//insert(end(),x)
}
入れる
ベクトル内の pos に要素を挿入するために使用される挿入関数の実装。
//插入
iterator insert(iterator pos, const T& x)
{
assert(pos >= _start && pos <= _finish);
//判断是否扩容
if (_finish == _end_of_storage)
{
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;
}
消す
Erase 関数の実装は、ベクトルの pos 位置にある指定された要素を削除するために使用されます。
//删除
iterator erase(iterator pos)
{
assert(pos >= _start && pos <= _finish);
iterator it = pos + 1;
while (it != _finish)
{
*(it - 1) = *it;
++it;
}
--_finish;
return pos;
}
完全に実現した
#pragma once
#include<assert.h>
namespace vec
{
//类模板
template<class T>
class vector
{
public:
//T类指针来实现迭代器
typedef T* iterator;
typedef const T* const_iterator;
//迭代器
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
//const修饰的迭代器
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
//构造函数,将空间填充为val对象
vector(size_t n, const T& val = T())
{
resize(n, val);
}
vector(int n, const T& val = T())
{
resize(n, val);
}
//构造函数,迭代区间进行构造
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{
while (first != last)
{
push_back(*first);
++first;
}
}
//空构造函数
vector()
{
}
构造函数
//vector()
// :_start(nullptr)
// ,_finish(nullptr)
// ,_end_of_storage(nullptr)
//{}
//深拷贝实现拷贝构造
vector(const vector<T>& v)
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
_start = new T[v.capacity()];
//memcpy(_start, v._start, sizeof(T) * v.size());
for (size_t i = 0; i < v.size(); i++)
{
_start[i] = v._start[i];
}
_finish = _start + v.size();
_end_of_storage = _start + v.capacity();
}
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(v);
return *this;
}
//析构函数
~vector()
{
if (_start != nullptr)
{
delete[] _start;
_start = nullptr;
_finish = nullptr;
_end_of_storage = nullptr;
}
}
//扩容函数capacity
void reserve(size_t n)
{
if (n > capacity())
{
size_t sz = size();
T* tmp = new T[n];
if (_start)
{
//如果是自定义类型,如果是string,则会出现浅拷贝
//memcpy(tmp, _start, sizeof(T) * sz);
for (size_t i = 0; i < sz; i++)
{
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = _start + sz;
_end_of_storage = _start + n;
}
}
//扩容size
void resize(size_t n, const T& val = T())
{
if (n < size())
{
_finish = _start + n;
}
else
{
reserve(n);
while (_finish != _start + n)
{
*_finish = val;
++_finish;
}
}
}
//尾插
void push_back(const T& v)
{
if (_finish == _end_of_storage)
{
size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
reserve(newcapacity);
}
*_finish = v;
++_finish;
//insert(end(),x)
}
//返回capacity
size_t capacity() const
{
return _end_of_storage - _start;
}
//返回size
size_t size() const
{
return _finish - _start;
}
//重载operator[] 可读可写
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
//重载operator[] 只读不可写
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 == _end_of_storage)
{
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 _end_of_storage = nullptr;
};
//打印
template<class T>
void print(const T& v)
{
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
}
テストコード
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
#include<string>
using namespace std;
#include"vector.h"
void test_vector1()
{
vec::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;
for (size_t i = 0; i < v1.size(); i++)
{
v1[i]++;
}
for (size_t i = 0; i < v1.size(); i++)
{
cout << v1[i] << " ";
}
cout << endl;
vec::print(v1);
}
void test_vector2()
{
vec::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;
v1.insert(v1.begin(), 10);
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
vec::vector<int>::iterator p = v1.begin() + 3;
//insert迭代器可能会失效
//insert建议不要使用这个形参迭代器
v1.insert(p, 100);
*p += 10;
vec::print(v1);
}
void test_vector3()
{
vec::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以后,迭代器失效了,不能访问it指向的空间
//因为vs会对其进行强制检查,访问会报错
cout << *it << endl;
++it;
cout << *it << endl;
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
}
void test_vector4()
{
vec::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;
vec::vector<int> v2(v1);
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
vec::vector<int> v3;
v3 = v1;
vec::print(v3);
}
void test_vector5()
{
vector<string> v1;
v1.push_back("11111");
v1.push_back("22222");
v1.push_back("33333");
v1.push_back("44444");
v1.push_back("55555");
for (auto& e : v1)
{
cout << e << " ";
}
cout << endl;
vector<string> v2(v1);
for (auto& e : v2)
{
cout << e << " ";
}
cout << endl;
}
int main()
{
//test_vector1();
//test_vector2();
//test_vector3();
//test_vector4();
test_vector5();
return 0;
}