- 迭代器是用来遍历某种数据结构的容器的
- 迭代器统一了接口,所以通过同样的接口不同的实现来迭代不同的容器
- 迭代器:记录一个指针,然后遍历就像移动指针一样,直到末尾
Vector类#
为Vector添加部分代码
class Vector
{
public:
//Vector的值类型
using ValueType = T;
//public 类型别名 (using/typedef):属于类(Class/Type)。它是一个“类型标签”,就像 Vector<int> 内部定义的一个子类型。
using Iterator = VectorIterator<Vector<T>>;
Iterator begin()
{
return Iterator(m_Data);
}
Iterator end()
{
return Iterator(m_Data + m_Size);
}
}Vector类完整代码
#pragma once
#ifdef LY_EP94
#include <memory>
#include <iostream>
//为某种类型(Vector)写迭代器
template<typename Vector>
class VectorIterator
{
public:
//Vector的值的类型
using ValueType = typename Vector::ValueType;
using PointerType = ValueType*;
using ReferenceType = ValueType&;
VectorIterator(PointerType ptr)
:m_Ptr(ptr)
{
}
//前缀运算符
VectorIterator& operator++()
{
m_Ptr++;
return *this;
}
//前缀运算符
VectorIterator& operator--()
{
m_Ptr--;
return *this;
}
//后缀运算符
//编译器用来区分前缀和后缀的唯一手段,int
//是语法占位符
VectorIterator& operator++(int)
{
//这里是复制,由于成员只有一个指针,代价可以接受
VectorIterator iterator = *this;
//前缀运算符
++(*this);
return iterator;
}
//后缀运算符
VectorIterator& operator--(int)
{
//这里是复制,由于成员只有一个指针,代价可以接受
VectorIterator iterator = *this;
//前缀运算符
--(*this);
return iterator;
}
ReferenceType operator[](int index)
{
return *(m_Ptr + index);
}
//C++ 对 -> 有特殊处理。当你使用 it->Method() 时,它会调用 it.operator->() 得到 m_Ptr,然后再次对结果应用 ->。所以最终执行的是 m_Ptr->Method()。
//所以这里需要返回一个指针
PointerType operator->()
{
return m_Ptr;
}
ReferenceType operator*()
{
return *m_Ptr;
}
bool operator==(const VectorIterator& other) const
{
return m_Ptr == other.m_Ptr;
}
bool operator!=(const VectorIterator& other) const
{
return !(*this == other);
}
private:
PointerType m_Ptr;
};
template<typename T>
class Vector
{
public:
//Vector的值类型
using ValueType = T;
//public 类型别名 (using/typedef):属于类(Class/Type)。它是一个“类型标签”,就像 Vector<int> 内部定义的一个子类型。
using Iterator = VectorIterator<Vector<T>>;
Iterator begin()
{
return Iterator(m_Data);
}
Iterator end()
{
return Iterator(m_Data + m_Size);
}
public:
Vector()
{
//allocate 2 elements
ReAlloc(2);
std::cout << "====构造器中初始化容量2======" << std::endl;
}
void PushBack(const T& value)
{
//如果当前数量量已经大于等于容量
if (m_Size >= m_Capacity)
{
//如果增长,容量扩大为1.5倍
ReAlloc(m_Capacity + m_Capacity / 2);
}
//m_Data[m_Size] 是一个已经存在的对象,所以
//这里调用的是拷贝赋值函数
//m_Data[m_Size] = value;
// new (&地址) 类型(参数)
new(&m_Data[m_Size]) T(value);
m_Size++;
std::cout << "添加元素:" << value << std::endl;
std::cout << "==================" << std::endl;
}
//1. PushBack的push右值引用的版本
//2. value 的类型始终是 Vector3&&(右值引用),
// 但它在函数体内的表现(属性)是一个左值
void PushBack(T&& value)
{
//如果当前数量量已经大于等于容量
if (m_Size >= m_Capacity)
{
//如果增长,容量扩大为1.5倍
ReAlloc(m_Capacity + m_Capacity / 2);
}
//1. m_Data[m_Size] 是一个已经存在的对象,所以
//这里调用的是移动赋值函数
//2. value 传入时是右值引用,但在函数内部它有了名
// 字,就变成了左值。为了触发移动赋值,
// 必须用 std::move 把它重新转回右值。
//3. 如果没有移动赋值函数,会去调用拷贝赋值函数
//m_Data[m_Size] = std::move(value);
// 使用移动构造函数在原始内存上创建对象
new(&m_Data[m_Size]) T(std::move(value));
m_Size++;
std::cout << "添加元素:" << value << std::endl;
std::cout << "==================" << std::endl;
}
//变长参数模板,三个点 ... 表示它可以接收
// 任意数量、任意类型的参数
template<typename... Args>
//&& 配合模板参数 Args 使用时,不再仅仅是
// 右值引用,它被称为万能引用,既能接收左值
// ,也能接收右值
T& EmplaceBack(Args&&... args)
{
//如果当前数量量已经大于等于容量
if (m_Size >= m_Capacity)
{
//如果增长,容量扩大为1.5倍
ReAlloc(m_Capacity + m_Capacity / 2);
}
//1. 参数一旦有了名字 args,它在函数内部就会变成左值。
//2. 解决:std::forward 的作用是:如果你传进来时是右值,
// 它就把它恢复成右值;如果你传进来时是左值,它就保持左
// 值。
//3. T(std::forward<Args>(args)...)这个是右值,调用移动
// 赋值函数后,马上destroy了
//m_Data[m_Size] = T(std::forward<Args>(args)...);
//使用placement new运算符,原地构造,就不会有对临时对象move以及destroy的操作了
new(&m_Data[m_Size]) T(std::forward<Args>(args)...);
std::cout << "添加元素:" << m_Data[m_Size] << std::endl;
std::cout << "==================" << std::endl;
//1. 取值(Evaluate):首先取出 m_Size 当前的值(比如现在是 0)。
//2. 定位(Access):利用这个旧值 0 找到 m_Data[0] 的引用,作为函数的返回值准备好。
//3. 自增(Increment):在这一行语句的逻辑完成,“返回”动作即将发生前(但还在函数体内),将 m_Size 的值加 1 变成 1。
return m_Data[m_Size++];
}
void PopBack()
{
if (m_Size > 0)
{
m_Size--;
//手动调用析构函数
m_Data[m_Size].~T();
}
}
void Clear()
{
for (size_t i = 0; i < m_Size; i++)
m_Data[i].~T();
m_Size = 0;
}
T& operator[](size_t index)
{
if (index >= m_Size)
{
//assert
}
return m_Data[index];
}
const T& operator[](size_t index) const
{
if (index >= m_Size)
{
//assert被设计为一种调试期工具,在发布版本会被去掉
//assert
}
return m_Data[index];
}
size_t Size() const { return m_Size; }
~Vector()
{
//会依次调用m_Data指向的所有对象的析构函数(
// 但是PopBack及Clear()已经调用过并已经释放了
// Vector3的m_MemoryBlock
Clear();
// delete[] m_Data;
::operator delete(m_Data, m_Capacity * sizeof(T));
}
private:
void ReAlloc(size_t newCapacity) {
// 1. allocate a new block of memory
// 2. copy/move old elements into new block
// 3. delete
//1. 尽可能低层次的访问内存,而不是智能指针
//2. 在堆上申请了一块连续(物理连续)的内存空间
//3. 在堆上创建了 newCapacity 个对象,并调用了它们
//的默认构造函数。也就是说,此时内存里已经存在了一
//堆“空”的 Vector3 对象
//T* newBlock = new T[newCapacity];
//分配原始字节内存,不调用任何构造函数
T* newBlock = (T*)::operator new(newCapacity * sizeof(T));
//如果新容量小于当前大小,即缩小容量
if (newCapacity < m_Size)
{
// 当前大小设置为新容量大小
m_Size = newCapacity;
std::cout << "缩小容量--" << std::endl;
}
else
{
std::cout << "增大容量--" << m_Capacity << "->" << newCapacity << std::endl;
}
for (size_t i = 0; i < m_Size; i++)
{
//不会触发复制构造函数
//memcpy(newBlock, m_Data, i * sizeof(T);
//复制,会触发复制构造函数
//newBlock[i] = std::move(m_Data[i]);
// 在 newBlock 的位置上,“构造”一个新对象,其内容移动自旧对象
new(&newBlock[i]) T(std::move(m_Data[i]));
}
//手动释放原来的对象
for (size_t i = 0; i < m_Size; i++)
{
m_Data[i].~T();
}
//1. 逐一析构:编译器会根据该内存块记录的大小信息(通常存储在数组头部的隐藏偏移量中),从后往前(或从前往后,取决于实现)调用每一个 Vector3 对象的析构函数。
//2. 释放内存:在所有对象的析构函数执行完毕后,才会一次性将整块堆内存归还给操作系统。
//delete[] m_Data;//释放原来指向的内存块
::operator delete(m_Data, m_Capacity * sizeof(T));
//delete m_Data;只会触发第一个元素的析构函数
m_Data = newBlock;//指向新的那个内存块
m_Capacity = newCapacity;
}
T* m_Data = nullptr;
//实际数组个数
size_t m_Size = 0;
//可分配存储的元素个数
size_t m_Capacity = 0;
};
#endifMain使用#
#ifdef LY_EP94
#include <iostream>
#include <vector>
#include "94_01_vector.h"
int main()
{
Vector<int> values;
values.EmplaceBack(1);
values.EmplaceBack(2);
values.EmplaceBack(3);
values.EmplaceBack(4);
values.EmplaceBack(5);
std::cout << "Not using iterators:\n";
for (int i = 0; i < values.Size(); i++)
{
std::cout << values[i] << std::endl;
}
std::cout << "Range-based for loop" << std::endl;
for (int value : values)
{
std::cout << value << std::endl;
}
std::cout << "Iterator:\n";
for (Vector<int>::Iterator it = values.begin();
it != values.end(); it++)
{
std::cout << *it << std::endl;
}
std::cin.get();
return 0;
}
#endif;