MyTinySTL项目学习笔记04

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

实现的功能:编译时根据类型来重载函数;具体而言:如果类型Titerator_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* 尝试获取类型 Uiterator_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双端队列

双端队列dequestackqueue的默认底层容器;

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中流出字符串时,就会覆盖原来的字符串;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OutlierLi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值