MyTinySTL项目学习笔记04
迭代器实现
迭代器类型
迭代器有多种类型,在之前的实现中,我们使用的迭代器类型包括:输入迭代器,输出迭代器,正向迭代器,双向迭代器,随机访问迭代器;
// 五种迭代器类型
struct input_iterator_tag {}; // 输入迭代器
struct output_iterator_tag {}; // 输出迭代器
struct forward_iterator_tag : public input_iterator_tag {}; // 正向迭代器
struct bidirectional_iterator_tag : public forward_iterator_tag {}; // 双向迭代器
struct random_access_iterator_tag : public bidirectional_iterator_tag {}; // 随机访问迭代器
可以看出,正向迭代器继承了输出迭代器,双向迭代器继承了正向迭代器,随机访问迭代器又继承了双向迭代器;
各类迭代器的区别:
- 输入迭代器:只能访问,不能修改;能递增;
- 输出迭代器:只能修改,不能访问值,能递增;
- 正向迭代器:除了输入迭代器所有功能外,增加了连续递增功能;
- 双向迭代器:除了正向迭代器功能外,增加了递减操作;
- 随机访问迭代器:结合了双向迭代器的所有功能,并且可以通过
operator[]
通过下标随机访问元素;支持迭代器之间的距离计算和比较;
检测迭代器类型特征(判断迭代器类型)
检测是否有迭代器特征标识
// 功能是用于检查一个类型 T 是否具有 iterator_category 类型的成员。
// 这个检查是通过模板特化和编译时类型特征检测实现的。
template <class T>
struct has_iterator_cat
{
private:
struct two { char a; char b; };
template <class U> static two test(...); // 静态模板函数,返回类型为two,传入任意数量参数
template <class U> static char test(typename U::iterator_category* = 0);
public:
static const bool value = sizeof(test<T>(0)) == sizeof(char);
};
实现的功能:编译时根据类型来重载函数;具体而言:如果类型T
有iterator_category*
成员变量,就会执行第二个test
函数,返回char
类型,然后将value
设置为true
;如果没有,则不会函数重载,只会执行第一个test
函数,返回一个two
类型,sizeof(two) != sizeof(char)
,导致value
设置为false
;
template <class U> static char test(typename U::iterator_category* = 0);
是一个静态模板函数,其中参数为typename U::iterator_category* = 0
,代表进行了类型检测;
typename U::iterator_category*
尝试获取类型 U
中 iterator_category
的地址。如果 U
包含 iterator_category
类型定义,这个表达式是有效的,有效的情况下默认值是0。如果无效,则不会重载该函数;
如此一来就实现了:如果传入类型U
有成员变量iterator_category*
,则会执行第二个test
函数,如果没有,则执行第一个test
函数,即实现了test
函数模板可以根据类型 U
是否具有 iterator_category
类型定义来重载;
用typename
实现重载时类型判断的例子:
// 定义一个类,它有一个 int 类型的成员变量
class MyClass {
public:
int value;
};
// 特化版本,使用 typename 来检查类型 T 是否具有 int 类型的成员变量
template <typename T>
void testFunctionWithMember(typename T::int_member_type& obj) {
std::cout << "Function accepting type with int member" << std::endl;
}
// 辅助函数,用于调用 testFunctionWithMember
template <typename T>
void callTestFunctionWithMember(T& obj) {
// 尝试调用 testFunctionWithMember,如果 T 没有 int_member_type 成员,这里将失败
testFunctionWithMember(obj);
}
int main() {
MyClass myObject;
callTestFunctionWithMember(myObject); // 将失败编译,因为 MyClass 没有 int_member_type
return 0;
}
获取迭代器特征标识
思路:
- 首先根据
has_iterator_cat()
检测是否有迭代器标识,该函数返回一个布尔类型; - 如果有迭代器标识,则通过
iterator_traits_helper
判断迭代器标识是否正确(输入迭代器还是输出迭代器,或者是这两个类型的派生类); - 如果正确,则会将该迭代器的所有信息提取出来,保存在
iterator_traits_impl
结构体中;
代码:
template <class Iterator, bool>
struct iterator_traits_impl {};
template <class Iterator>
struct iterator_traits_impl<Iterator, true>
{
typedef typename Iterator::iterator_category iterator_category;
typedef typename Iterator::value_type value_type;
typedef typename Iterator::pointer pointer;
typedef typename Iterator::reference reference;
typedef typename Iterator::difference_type difference_type;
};
template <class Iterator, bool>
struct iterator_traits_helper {};
template <class Iterator>
struct iterator_traits_helper<Iterator, true>
: public iterator_traits_impl<Iterator,
std::is_convertible<typename Iterator::iterator_category, input_iterator_tag>::value ||
std::is_convertible<typename Iterator::iterator_category, output_iterator_tag>::value> {};
// 萃取迭代器的特性
template <class Iterator>
struct iterator_traits
: public iterator_traits_helper<Iterator, has_iterator_cat<Iterator>::value> {};
iterator_traits_impl
是一个模板结构体,它有两个模板参数:Iterator
和一个布尔类型的 bool
。这个结构体的实现使用了一个条件编译技术,它依赖于第二个模板参数的值。这里的设计模式通常被称为“类型特征”或“SFINAE(Substitution Failure Is Not An Error)”。
SFINAE(Substitution Failure Is Not An Error,替换失败不是错误)原则:对于第二参数提供了true
的时候,第二个模板会替换失败,但是这不是错误,所以会忽略第二个特化模板,去执行第一个特化模板;(编译器将忽略这个特化,并可能寻找其他适用的特化或回退到默认模板定义)
// 第一个模板特化
template <class Iterator, bool>
struct iterator_traits_impl {};
这个定义是一个空的结构体模板,它作为默认情况,当第二个模板参数为 false
时被选择。由于这里没有提供成员定义,它实际上不提供任何信息或者功能。
// 第二个模板特化
template <class Iterator>
struct iterator_traits_impl<Iterator, true>
{
typedef typename Iterator::iterator_category iterator_category;
typedef typename Iterator::value_type value_type;
typedef typename Iterator::pointer pointer;
typedef typename Iterator::reference reference;
typedef typename Iterator::difference_type difference_type;
};
这个特化版本是当第二个模板参数为 true
时被选择的。它提供了实际的类型定义,这些类型是从 Iterator
类型中提取出来的。
空的结构体用作SFINAE的一部分。如果尝试实例化第一个模板而失败(即,如果 Iterator
类型不提供所需的成员),编译器将尝试第二个模板特化。这允许开发者基于类型特征来选择正确的模板实例化。
// 如果定义一个总是模板特化的结构体
template <class Iterator>
struct iterator_traits_impl <Iterator, false> {};
单一模板参数class Iterator
无法实现根据bool
类型执行不同的模板特化;
过程:首先如果传入了bool
类型的参数,会优先判断第二个模板是否匹配,如果匹配则执行,如果不匹配,则执行第一个模板,第一个模板的bool
默认是false
;
全特化和部分特化:对于第一个模板特化,只是部分特化,它依赖于一个布尔模板参数,但是这个布尔参数没有在显式实例化时给出具体值;而第二个模板特化是一个全特化,它显式地将第二个模板参数设置为 true
。这意味着它将只匹配当第二个参数为 true
的情况。
// 针对原生指针的偏特化版本
template <class T>
struct iterator_traits<T*>
{
typedef random_access_iterator_tag iterator_category;
typedef T value_type;
typedef T* pointer;
typedef T& reference;
typedef ptrdiff_t difference_type;
};
原生指针会被当做随机迭代器类型;
萃取某种迭代器类型
template <class T, class U, bool = has_iterator_cat<iterator_traits<T>>::value>
struct has_iterator_cat_of
: public m_bool_constant<std::is_convertible<
typename iterator_traits<T>::iterator_category, U>::value>
{};
// 萃取某种迭代器
template <class T, class U>
struct has_iterator_cat_of<T, U, false> : public m_false_type {};
template <class Iter>
struct is_input_iterator : public has_iterator_cat_of<Iter, input_iterator_tag> {};
template <class Iter>
struct is_output_iterator : public has_iterator_cat_of<Iter, output_iterator_tag> {};
template <class Iter>
struct is_forward_iterator : public has_iterator_cat_of<Iter, forward_iterator_tag> {};
template <class Iter>
struct is_bidirectional_iterator : public has_iterator_cat_of<Iter, bidirectional_iterator_tag> {};
template <class Iter>
struct is_random_access_iterator : public has_iterator_cat_of<Iter, random_access_iterator_tag> {};
template <class Iterator>
struct is_iterator :
public m_bool_constant<is_input_iterator<Iterator>::value ||
is_output_iterator<Iterator>::value> {};
将传入迭代器的iterator_category
和迭代器类型比较;判断迭代器类型;
// 萃取某个迭代器的 value_type
template <class Iterator>
typename iterator_traits<Iterator>::value_type*
value_type(const Iterator&)
{
return static_cast<typename iterator_traits<Iterator>::value_type*>(0);
}
通过iterator_traits<Iterator>
保存了合法迭代器的信息,然后通过::
域作用运算符获取value_type*
;
反向迭代器
使前进为后退,后退为前进;
对于正向迭代器进行功能封装,实现反向迭代器;
// 前进(++)变为后退(--)
self& operator++()
{
--current;
return *this;
}
重载自增运算符,使得++
在实际上完成--
的功能;重载其他运算符,例如+=
,--
,-=
等;
deque双端队列
双端队列deque
是stack
和queue
的默认底层容器;
template <class T>
class deque
{
public:
...
private:
// 用以下四个数据来表现一个 deque
iterator begin_; // 指向第一个节点
iterator end_; // 指向最后一个结点
map_pointer map_; // 指向一块 map,map 中的每个元素都是一个指针,指向一个缓冲区
size_type map_size_; // map 内指针的数目
public:
// 构造、复制、移动、析构函数
deque()
{ fill_init(0, value_type()); }
explicit deque(size_type n)
{ fill_init(n, value_type()); }
deque(size_type n, const value_type& value)
{ fill_init(n, value); }
...
}
迭代器继承自随机访问迭代器;
析构函数:
~deque()
{
if (map_ != nullptr)
{
clear();
data_allocator::deallocate(*begin_.node, buffer_size);
*begin_.node = nullptr;
map_allocator::deallocate(map_, map_size_);
map_ = nullptr;
}
}
手动释放内存空间;
可以按照秩访问元素,即operator[]
来访问对应位置的元素;
在指定位置插入元素时,先判断是不是在头部或者尾部,如果在头部或者尾部插入,则会简单很多,可以单独处理;如果不是,则需要判断是前半段插入还是后半段插入(判断之后就可以知道是从头部找插入位置,还是从尾部找,找离插入位置近的一端)
// insert_aux 函数(在中间插入元素)
template <class T>
template <class... Args>
typename deque<T>::iterator
deque<T>::
insert_aux(iterator position, Args&& ...args)
{
const size_type elems_before = position - begin_;
value_type value_copy = value_type(mystl::forward<Args>(args)...);
if (elems_before < (size() / 2))
{ // 在前半段插入
emplace_front(front());
auto front1 = begin_;
++front1;
auto front2 = front1;
++front2;
position = begin_ + elems_before;
auto pos = position;
++pos;
mystl::copy(front2, pos, front1);
}
else
{ // 在后半段插入
emplace_back(back());
auto back1 = end_;
--back1;
auto back2 = back1;
--back2;
position = begin_ + elems_before;
mystl::copy_backward(position, back2, back1);
}
*position = mystl::move(value_copy);
return position;
}
// copy调用了unchecked_copy,copy把 [first, last)区间内的元素拷贝到 [result, result + (last - first))内
unchecked_copy(Tp* first, Tp* last, Up* result)
{
const auto n = static_cast<size_t>(last - first);
if (n != 0)
std::memmove(result, first, n * sizeof(Up));
return result + n;
}
list的实现
list
是双向链表;
list的节点结构
template <class T>
struct list_node_base
{
typedef typename node_traits<T>::base_ptr base_ptr;
typedef typename node_traits<T>::node_ptr node_ptr;
base_ptr prev; // 前一节点
base_ptr next; // 下一节点
list_node_base() = default;
node_ptr as_node() {return static_cast<node_ptr>(self());}
void unlink() {prev = next = self();}
base_ptr self() {return static_cast<base_ptr>(&*this);}
};
template <class T>
struct list_node : public list_node_base<T>
{
typedef typename node_traits<T>::base_ptr base_ptr;
typedef typename node_traits<T>::node_ptr node_ptr;
T value; // 数据域
list_node() = default;
list_node(const T& v) : value(v) {}
list_node(T&& v) : value(mystl::move(v)) {}
base_ptr as_base() {return static_cast<base_ptr>(&*this);}
node_ptr self() {return static_cast<node_ptr>(&*this);}
};
有数据域变量T value
,也有前一节点指针base_ptr prev
以及后一个节点指针base_ptr next
;
迭代器设计以及重载操作符
// list 的迭代器设计
template <class T>
struct list_iterator : public mystl::iterator<mystl::bidirectional_iterator_tag, T>
{
typedef T value_type;
typedef T* pointer;
typedef T& reference;
typedef typename node_traits<T>::base_ptr base_ptr;
typedef typename node_traits<T>::node_ptr node_ptr;
typedef list_iterator<T> self;
base_ptr node_; // 指向当前节点
// 构造函数
list_iterator() = default;
list_iterator(base_ptr x)
:node_(x) {}
list_iterator(node_ptr x)
:node_(x->as_base()) {}
list_iterator(const list_iterator& rhs)
:node_(rhs.node_) {}
...
}
当前节点的指针为node_
;
// 重载操作符
reference operator*() const { return node_->as_node()->value; }
pointer operator->() const { return &(operator*()); }
重载指针操作符,返回解引用的数据;
self& operator++()
{
MYSTL_DEBUG(node_ != nullptr);
node_ = node_->next;
return *this;
}
自增操作符是获取当前节点node_
的下一个节点,即next
指针指向的节点;
list模板类
template <class T>
class list
{
public:
...
private:
base_ptr node_; // 指向末尾节点
size_type size_; // 大小
public:
// 构造、复制、移动、析构函数
list() { fill_init(0, value_type()); }
...
~list()
{
if (node_) // 如果当前节点存在,即链表不为空
{
clear();
base_allocator::deallocate(node_); // 释放空间
node_ = nullptr; // 避免野指针
size_ = 0; // 修改成员变量
}
}
}
// 迭代器相关操作
iterator begin() noexcept
{ return node_->next; }
const_iterator begin() const noexcept
{ return node_->next; }
iterator end() noexcept
{ return node_; }
const_iterator end() const noexcept
{ return node_; }
node_
实际上被当成了哨兵节点,并不存储实际元素,而从node_->next
开始才存储实际元素;
插入节点
iterator insert(const_iterator pos, const value_type& value)
{
THROW_LENGTH_ERROR_IF(size_ > max_size() - 1, "list<T>'s size too big");
auto link_node = create_node(value);
++size_;
return link_iter_node(pos, link_node->as_base());
}
插入节点首先要创建一个新节点,即create_node(value)
,然后将节点插入到对应位置;
// 创建结点
template <class T>
template <class ...Args>
typename list<T>::node_ptr
list<T>::create_node(Args&& ...args)
{
node_ptr p = node_allocator::allocate(1);
try
{
data_allocator::construct(mystl::address_of(p->value), mystl::forward<Args>(args)...);
p->prev = nullptr;
p->next = nullptr;
}
catch (...)
{
node_allocator::deallocate(p);
throw;
}
return p;
}
创建节点需要先分配空间,然后将节点的两个指针成员变量初始化;
// 在 pos 处连接一个节点
template <class T>
typename list<T>::iterator
list<T>::link_iter_node(const_iterator pos, base_ptr link_node)
{
if (pos == node_->next)
{
link_nodes_at_front(link_node, link_node);
}
else if (pos == node_)
{
link_nodes_at_back(link_node, link_node);
}
else
{
link_nodes(pos.node_, link_node, link_node);
}
return iterator(link_node);
}
// 在 pos 处连接 [first, last] 的结点
template <class T>
void list<T>::link_nodes(base_ptr pos, base_ptr first, base_ptr last)
{
pos->prev->next = first;
first->prev = pos->prev;
pos->prev = last;
last->next = pos;
}
注意双向链表的插入过程;
键值对结构体
// 结构体模板 : pair
// 两个模板参数分别表示两个数据的类型
// 用 first 和 second 来分别取出第一个数据和第二个数据
template <class Ty1, class Ty2>
struct pair
{
typedef Ty1 first_type;
typedef Ty2 second_type;
first_type first; // 保存第一个数据
second_type second; // 保存第二个数据
// 构造函数、
...
// 重载运算符
// copy assign for this pair
pair& operator=(const pair& rhs)
{
if (this != &rhs)
{
first = rhs.first;
second = rhs.second;
}
return *this;
}
...
// 全局函数,让两个数据成为一个 pair
template <class Ty1, class Ty2>
pair<Ty1, Ty2> make_pair(Ty1&& first, Ty2&& second)
{
return pair<Ty1, Ty2>(mystl::forward<Ty1>(first), mystl::forward<Ty2>(second));
}
}
注意make_pair()
的实现,传入的是右值引用(常量),通过两个常量构造一个键值对;
字符串实现
template <class CharType, class CharTraits = mystl::char_traits<CharType>>
class basic_string
{
public:
// 末尾位置的值,例:
// if (str.find('a') != string::npos) { /* do something */ }
static constexpr size_type npos = static_cast<size_type>(-1);
private:
iterator buffer_; // 储存字符串的起始位置
size_type size_; // 大小
size_type cap_; // 容量
public:
// 构造、复制、移动、析构函数
basic_string() noexcept
{ try_init(); }
...
~basic_string() { destroy_buffer(); }
...
}
迭代器相关操作:
// 迭代器相关操作
iterator begin() noexcept
{ return buffer_; }
iterator end() noexcept
{ return buffer_ + size_; }
容器相关操作:
// 容量相关操作
bool empty() const noexcept
{ return size_ == 0; }
size_type size() const noexcept
{ return size_; }
size_type length() const noexcept
{ return size_; }
size_type capacity() const noexcept
{ return cap_; }
size_type max_size() const noexcept
{ return static_cast<size_type>(-1); }
void reserve(size_type n);
void shrink_to_fit();
注意size()
和length()
都是返回size_
,而长度和容量不同,容量cap_
是包括还未利用的空间;
访问元素相关操作:
// 访问元素相关操作
reference operator[](size_type n)
{
MYSTL_DEBUG(n <= size_); // 没有越界
if (n == size_)
*(buffer_ + n) = value_type();
return *(buffer_ + n);
}
reference at(size_type n)
{
THROW_OUT_OF_RANGE_IF(n >= size_, "basic_string<Char, Traits>::at()"
"subscript out of range");
return (*this)[n];
}
// push_back / pop_back
void push_back(value_type ch) { append(1, ch); }
void pop_back()
{
MYSTL_DEBUG(!empty());
--size_;
}
void clear() noexcept { size_ = 0; }
注意clear()
操作没有释放空间以及将空间内值都恢复默认值,而是直接将size_
变成0;
所以删除并不是真的删除,只是调整了内部成员变量让我们无法访问到,底层内存空间内没有被覆盖;
// substr
basic_string substr(size_type index, size_type count = npos)
{
count = mystl::min(count, size_ - index);
return basic_string(buffer_ + index, buffer_ + index + count);
}
获取子串,传入子串开始索引以及子串长度;
查找相关操作:
// find
size_type find(value_type ch, size_type pos = 0) const noexcept;
size_type find(const_pointer str, size_type pos = 0) const noexcept;
size_type find(const_pointer str, size_type pos, size_type count) const noexcept;
size_type find(const basic_string& str, size_type pos = 0) const noexcept;
// find_first_of
size_type find_first_of(value_type ch, size_type pos = 0) const noexcept;
size_type find_first_of(const_pointer s, size_type pos = 0) const noexcept;
size_type find_first_of(const_pointer s, size_type pos, size_type count) const noexcept;
size_type find_first_of(const basic_string& str, size_type pos = 0) const noexcept;
查找操作并不是返回所有匹配的子串,而是只返回第一个匹配的子串的开始索引;
重载+=
运算符用于字符串拼接:
// 重载 operator+=
basic_string& operator+=(const basic_string& str)
{ return append(str); }
basic_string& operator+=(value_type ch)
{ return append(1, ch); }
basic_string& operator+=(const_pointer str)
{ return append(str, str + char_traits::length(str)); }
重载>>
和<<
流运算符:
// 重载 operator >> / operatror <<
friend std::istream& operator >> (std::istream& is, basic_string& str)
{
value_type* buf = new value_type[4096];
is >> buf;
basic_string tmp(buf);
str = std::move(tmp); // 将tmp转化为右值引用,然后赋值给str
delete[]buf;
return is;
}
friend std::ostream& operator << (std::ostream& os, const basic_string& str)
{
for (size_type i = 0; i < str.size_; ++i)
os << *(str.buffer_ + i);
return os;
}
当字符串部分流入ostream
时,该部分就会从字符串中移除;当从istream
中流出字符串时,就会覆盖原来的字符串;