第2章:vector - 動的配列
はじめに
vectorは最も基本的なSTLコンテナです。連続したメモリ領域に要素を格納し、動的にサイズを変更できます。
---
1. vectorの特性
1.1 データ構造
vector内部:
┌─────┬─────┬─────┬─────┬─────┬─────┐
│ 1 │ 2 │ 3 │ 4 │ │ │
└─────┴─────┴─────┴─────┴─────┴─────┘
↑ ↑ ↑
_data _size _capacity
_data: 配列の先頭へのポインタ
_size: 実際の要素数
_capacity: 確保されたメモリ容量
1.2 計算量
| 操作 | 計算量 | |------|--------| | operator[] | O(1) | | push_back | 償却O(1) | | pop_back | O(1) | | insert | O(n) | | erase | O(n) | | begin/end | O(1) | | size/capacity | O(1) |
1.3 メモリ再確保戦略
// 典型的な戦略: 2倍ずつ増やす
size_t new_capacity = _capacity ? _capacity * 2 : 1;
// なぜ2倍?
// - 1.5倍: メモリ効率は良いが、再確保が多い
// - 2倍: 再確保回数とメモリ効率のバランス
// - 3倍以上: メモリを無駄に使う
// 償却計算量がO(1)になる条件:
// 増加率 > 1 であれば、n回のpush_backの総コストはO(n)
---
2. 基本実装
2.1 クラス定義
namespace ft {
template<typename T, typename Alloc = std::allocator<T> >
class vector {
public:
// 型定義
typedef T value_type;
typedef Alloc allocator_type;
typedef typename allocator_type::reference reference;
typedef typename allocator_type::const_reference const_reference;
typedef typename allocator_type::pointer pointer;
typedef typename allocator_type::const_pointer const_pointer;
typedef typename allocator_type::size_type size_type;
typedef typename allocator_type::difference_type difference_type;
// イテレータ型
typedef pointer iterator;
typedef const_pointer const_iterator;
typedef ft::reverse_iterator<iterator> reverse_iterator;
typedef ft::reverse_iterator<const_iterator> const_reverse_iterator;
private:
allocator_type _alloc;
pointer _data;
size_type _size;
size_type _capacity;
public:
// コンストラクタ・デストラクタ
explicit vector(const allocator_type& alloc = allocator_type());
explicit vector(size_type n, const value_type& val = value_type(),
const allocator_type& alloc = allocator_type());
template<typename InputIterator>
vector(InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type());
vector(const vector& x);
~vector();
vector& operator=(const vector& x);
// イテレータ
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
reverse_iterator rend();
const_reverse_iterator rend() const;
// 容量
size_type size() const;
size_type max_size() const;
void resize(size_type n, value_type val = value_type());
size_type capacity() const;
bool empty() const;
void reserve(size_type n);
// 要素アクセス
reference operator[](size_type n);
const_reference operator[](size_type n) const;
reference at(size_type n);
const_reference at(size_type n) const;
reference front();
const_reference front() const;
reference back();
const_reference back() const;
// 変更
template<typename InputIterator>
void assign(InputIterator first, InputIterator last);
void assign(size_type n, const value_type& val);
void push_back(const value_type& val);
void pop_back();
iterator insert(iterator position, const value_type& val);
void insert(iterator position, size_type n, const value_type& val);
template<typename InputIterator>
void insert(iterator position, InputIterator first, InputIterator last);
iterator erase(iterator position);
iterator erase(iterator first, iterator last);
void swap(vector& x);
void clear();
// アロケータ
allocator_type get_allocator() const;
};
} // namespace ft
2.2 コンストラクタとデストラクタ
// デフォルトコンストラクタ
template<typename T, typename Alloc>
vector<T, Alloc>::vector(const allocator_type& alloc)
: _alloc(alloc), _data(NULL), _size(0), _capacity(0) {}
// fillコンストラクタ
template<typename T, typename Alloc>
vector<T, Alloc>::vector(size_type n, const value_type& val,
const allocator_type& alloc)
: _alloc(alloc), _data(NULL), _size(0), _capacity(0) {
if (n > 0) {
_data = _alloc.allocate(n);
_capacity = n;
for (size_type i = 0; i < n; ++i) {
_alloc.construct(_data + i, val);
++_size;
}
}
}
// rangeコンストラクタ
template<typename T, typename Alloc>
template<typename InputIterator>
vector<T, Alloc>::vector(InputIterator first, InputIterator last,
const allocator_type& alloc)
: _alloc(alloc), _data(NULL), _size(0), _capacity(0) {
// is_integralを使って、fillコンストラクタとの衝突を解決
typedef typename ft::is_integral<InputIterator>::type Integral;
_init_range(first, last, Integral());
}
// ヘルパー関数
template<typename T, typename Alloc>
template<typename InputIterator>
void vector<T, Alloc>::_init_range(InputIterator first, InputIterator last,
ft::false_type) {
// 本当のイテレータの場合
for (; first != last; ++first) {
push_back(*first);
}
}
template<typename T, typename Alloc>
template<typename Integer>
void vector<T, Alloc>::_init_range(Integer n, Integer val, ft::true_type) {
// 整数型の場合(fillコンストラクタとして扱う)
_data = _alloc.allocate(n);
_capacity = n;
for (size_type i = 0; i < static_cast<size_type>(n); ++i) {
_alloc.construct(_data + i, val);
++_size;
}
}
// コピーコンストラクタ
template<typename T, typename Alloc>
vector<T, Alloc>::vector(const vector& x)
: _alloc(x._alloc), _data(NULL), _size(0), _capacity(0) {
*this = x;
}
// デストラクタ
template<typename T, typename Alloc>
vector<T, Alloc>::~vector() {
clear();
if (_data) {
_alloc.deallocate(_data, _capacity);
}
}
// 代入演算子
template<typename T, typename Alloc>
vector<T, Alloc>& vector<T, Alloc>::operator=(const vector& x) {
if (this != &x) {
clear();
if (_capacity < x._size) {
if (_data) {
_alloc.deallocate(_data, _capacity);
}
_data = _alloc.allocate(x._size);
_capacity = x._size;
}
for (size_type i = 0; i < x._size; ++i) {
_alloc.construct(_data + i, x._data[i]);
}
_size = x._size;
}
return *this;
}
2.3 イテレータ
template<typename T, typename Alloc>
typename vector<T, Alloc>::iterator vector<T, Alloc>::begin() {
return _data;
}
template<typename T, typename Alloc>
typename vector<T, Alloc>::const_iterator vector<T, Alloc>::begin() const {
return _data;
}
template<typename T, typename Alloc>
typename vector<T, Alloc>::iterator vector<T, Alloc>::end() {
return _data + _size;
}
template<typename T, typename Alloc>
typename vector<T, Alloc>::const_iterator vector<T, Alloc>::end() const {
return _data + _size;
}
template<typename T, typename Alloc>
typename vector<T, Alloc>::reverse_iterator vector<T, Alloc>::rbegin() {
return reverse_iterator(end());
}
template<typename T, typename Alloc>
typename vector<T, Alloc>::reverse_iterator vector<T, Alloc>::rend() {
return reverse_iterator(begin());
}
---
3. 容量管理
3.1 size, capacity, max_size
template<typename T, typename Alloc>
typename vector<T, Alloc>::size_type vector<T, Alloc>::size() const {
return _size;
}
template<typename T, typename Alloc>
typename vector<T, Alloc>::size_type vector<T, Alloc>::capacity() const {
return _capacity;
}
template<typename T, typename Alloc>
typename vector<T, Alloc>::size_type vector<T, Alloc>::max_size() const {
return _alloc.max_size();
}
template<typename T, typename Alloc>
bool vector<T, Alloc>::empty() const {
return _size == 0;
}
3.2 reserve
template<typename T, typename Alloc>
void vector<T, Alloc>::reserve(size_type n) {
if (n > max_size()) {
throw std::length_error("vector::reserve");
}
if (n > _capacity) {
pointer new_data = _alloc.allocate(n);
// 既存要素をコピー
for (size_type i = 0; i < _size; ++i) {
_alloc.construct(new_data + i, _data[i]);
_alloc.destroy(_data + i);
}
// 古いメモリを解放
if (_data) {
_alloc.deallocate(_data, _capacity);
}
_data = new_data;
_capacity = n;
}
}
3.3 resize
template<typename T, typename Alloc>
void vector<T, Alloc>::resize(size_type n, value_type val) {
if (n < _size) {
// 縮小:余分な要素を破棄
while (_size > n) {
_alloc.destroy(_data + _size - 1);
--_size;
}
} else if (n > _size) {
// 拡大:必要に応じてメモリを確保
if (n > _capacity) {
reserve(n);
}
// 新しい要素を構築
while (_size < n) {
_alloc.construct(_data + _size, val);
++_size;
}
}
}
---
4. 要素アクセス
4.1 operator[] と at
template<typename T, typename Alloc>
typename vector<T, Alloc>::reference
vector<T, Alloc>::operator[](size_type n) {
return _data[n];
}
template<typename T, typename Alloc>
typename vector<T, Alloc>::const_reference
vector<T, Alloc>::operator[](size_type n) const {
return _data[n];
}
template<typename T, typename Alloc>
typename vector<T, Alloc>::reference
vector<T, Alloc>::at(size_type n) {
if (n >= _size) {
throw std::out_of_range("vector::at");
}
return _data[n];
}
template<typename T, typename Alloc>
typename vector<T, Alloc>::const_reference
vector<T, Alloc>::at(size_type n) const {
if (n >= _size) {
throw std::out_of_range("vector::at");
}
return _data[n];
}
4.2 front と back
template<typename T, typename Alloc>
typename vector<T, Alloc>::reference vector<T, Alloc>::front() {
return _data[0];
}
template<typename T, typename Alloc>
typename vector<T, Alloc>::reference vector<T, Alloc>::back() {
return _data[_size - 1];
}
---
5. 要素の変更
5.1 push_back と pop_back
template<typename T, typename Alloc>
void vector<T, Alloc>::push_back(const value_type& val) {
if (_size == _capacity) {
size_type new_cap = _capacity ? _capacity * 2 : 1;
reserve(new_cap);
}
_alloc.construct(_data + _size, val);
++_size;
}
template<typename T, typename Alloc>
void vector<T, Alloc>::pop_back() {
if (_size > 0) {
_alloc.destroy(_data + _size - 1);
--_size;
}
}
5.2 insert
template<typename T, typename Alloc>
typename vector<T, Alloc>::iterator
vector<T, Alloc>::insert(iterator position, const value_type& val) {
size_type pos = position - begin();
if (_size == _capacity) {
size_type new_cap = _capacity ? _capacity * 2 : 1;
reserve(new_cap);
}
// positionをreserve後に再計算
position = begin() + pos;
// 後ろの要素を1つずつ後ろにずらす
for (iterator it = end(); it != position; --it) {
_alloc.construct(&(*it), *(it - 1));
_alloc.destroy(it - 1);
}
// 新しい要素を挿入
_alloc.construct(&(*position), val);
++_size;
return position;
}
template<typename T, typename Alloc>
void vector<T, Alloc>::insert(iterator position, size_type n,
const value_type& val) {
if (n == 0) return;
size_type pos = position - begin();
if (_size + n > _capacity) {
size_type new_cap = _size + n;
if (new_cap < _capacity * 2) {
new_cap = _capacity * 2;
}
reserve(new_cap);
}
position = begin() + pos;
// 後ろの要素をn個分後ろにずらす
for (iterator it = end() + n - 1; it >= position + n; --it) {
_alloc.construct(&(*it), *(it - n));
_alloc.destroy(it - n);
}
// 新しい要素を挿入
for (size_type i = 0; i < n; ++i) {
_alloc.construct(&(*(position + i)), val);
}
_size += n;
}
5.3 erase
template<typename T, typename Alloc>
typename vector<T, Alloc>::iterator
vector<T, Alloc>::erase(iterator position) {
_alloc.destroy(&(*position));
// 後ろの要素を前にずらす
for (iterator it = position; it + 1 != end(); ++it) {
_alloc.construct(&(*it), *(it + 1));
_alloc.destroy(it + 1);
}
--_size;
return position;
}
template<typename T, typename Alloc>
typename vector<T, Alloc>::iterator
vector<T, Alloc>::erase(iterator first, iterator last) {
size_type n = last - first;
// 範囲内の要素を破棄
for (iterator it = first; it != last; ++it) {
_alloc.destroy(&(*it));
}
// 後ろの要素を前にずらす
for (iterator it = first; it + n != end(); ++it) {
_alloc.construct(&(*it), *(it + n));
_alloc.destroy(it + n);
}
_size -= n;
return first;
}
5.4 clear と swap
template<typename T, typename Alloc>
void vector<T, Alloc>::clear() {
while (_size > 0) {
pop_back();
}
}
template<typename T, typename Alloc>
void vector<T, Alloc>::swap(vector& x) {
std::swap(_alloc, x._alloc);
std::swap(_data, x._data);
std::swap(_size, x._size);
std::swap(_capacity, x._capacity);
}
---
6. 比較演算子
template<typename T, typename Alloc>
bool operator==(const vector<T, Alloc>& lhs, const vector<T, Alloc>& rhs) {
if (lhs.size() != rhs.size()) {
return false;
}
return ft::equal(lhs.begin(), lhs.end(), rhs.begin());
}
template<typename T, typename Alloc>
bool operator!=(const vector<T, Alloc>& lhs, const vector<T, Alloc>& rhs) {
return !(lhs == rhs);
}
template<typename T, typename Alloc>
bool operator<(const vector<T, Alloc>& lhs, const vector<T, Alloc>& rhs) {
return ft::lexicographical_compare(
lhs.begin(), lhs.end(),
rhs.begin(), rhs.end()
);
}
template<typename T, typename Alloc>
bool operator<=(const vector<T, Alloc>& lhs, const vector<T, Alloc>& rhs) {
return !(rhs < lhs);
}
template<typename T, typename Alloc>
bool operator>(const vector<T, Alloc>& lhs, const vector<T, Alloc>& rhs) {
return rhs < lhs;
}
template<typename T, typename Alloc>
bool operator>=(const vector<T, Alloc>& lhs, const vector<T, Alloc>& rhs) {
return !(lhs < rhs);
}
// 非メンバswap
template<typename T, typename Alloc>
void swap(vector<T, Alloc>& x, vector<T, Alloc>& y) {
x.swap(y);
}
---
まとめ
本章で学んだこと:
- vectorの特性: 連続メモリ、動的サイズ
- メモリ管理: reserve, resize, capacityの関係
- 要素アクセス: operator[], at, front, back
- 要素の変更: push_back, pop_back, insert, erase
- 例外安全性: 強い保証を提供する実装
次章では、map/set(赤黒木)の実装を学びます。