在C++20的标准下实现自己的vector容器
首先介绍一下vector
的基本功能:vector
是一个线性容器,他是一种动态数组,其内存管理是动态的,但元素的访问方式类似于静态数组(即通过索引直接访问)
相关知识介绍:
std::allocator
: 在所有的标准stl容器中,都是使用allocator
来管理内存,这种管理方式只会申请原始内存,即没有初始化。(不同于new,申请内存的同时会在堆上构建 ==不使用new的原因是默认的new效率更低,同时使用allocator可以更好的控制对象的生命周期,允许在已分配的内存中按需构造析构对象==)。同时使用std::allocator_traits<std::allocator<T>>::construct
和std::allocator_traits<std::allocator<T>>::destory
来显示的构造或者析构对象。- 类的基本知识:在C++ 11之后,新建一个类,哪怕没有声明任何一个函数,编译器会默认实现六个函数,分别是:默认构造函数,默认析构函数,拷贝构造函数,移动构造函数,拷贝赋值函数,移动赋值函数。
- 迭代器:在类中,再去定义一个迭代器类,来封装一个容器内对象类型的指针,同时重载一些运算符的实现
- 动态内存:vector在堆上新申请一片内存,当这个内存被用满之后,那么vector就要重新去申请一片内存,然后把之前的元素全部放入。
1 |
|
vector的私有属性
1 | T* data;//指向管理的数据的指针 |
vector的迭代器
1 | class iterator { |
在C++11之后,除了iterator
之后,还实现了const_iterator
。迭代器内实现了各种常见指针操作的重载
vector的扩容操作
在执行push_back,emplace_back, insert等操作后,vector可能需要扩容,此时需要调用reserve函数去堆上申请一块新的内存空间。在vector扩容的时候,通常扩充为之前的1.5~2倍大小
1 | template <typename T> |
vector的构造函数
1 | Vector() noexcept; |
首先是默认构造
Vector() noexcept
, noexcept表示不会抛出异常然后是单参构造函数
explicit Vector(size_t n)
,在C++11之前,对于单参构造函数,都要用explicit
来避免隐式转化。在C++11之后,也可以堆多参构造函数使用拷贝构造函数
Vector(const Vector<T>& other);
1
2
3
4
5
6
7
8template <typename T>
Vector<T>::Vector(const Vector<T>& other) : data(nullptr), vec_size(0), vec_capacity(0) {
reserve(other.vec_size);
for (size_t i = 0; i < other.vec_size; ++i) {
std::allocator_traits<std::allocator<T>>::construct(allocator, &data[i], other.data[i]);
}
vec_size = other.vec_size;
}移动构造函数
Vector<T>::Vector(Vector<T>&& other) noexcept :
1
2
3
4
5
6
7
8
9
10
11
12// 移动构造函数实现
template <typename T>
Vector<T>::Vector(Vector<T>&& other) noexcept :
data(other.data),
vec_size(other.vec_size),
vec_capacity(other.vec_capacity),
allocator(std::move(other.allocator)) {
// 防止 other 析构时释放我们刚"偷"来的内存
other.data = nullptr;
other.vec_size = 0;
other.vec_capacity = 0;
}初始化列表构造
Vector(std::initializer_list<T> init);
:1
2
3
4
5
6
7
8
9
10
11// 初始化列表构造函数实现
template <typename T>
Vector<T>::Vector(std::initializer_list<T> init) : data(nullptr), vec_size(0), vec_capacity(0) {
if (init.size() > 0) {
reserve(init.size());
for (size_t i = 0; i < init.size(); ++i) {
std::allocator_traits<std::allocator<T>>::construct(allocator, &data[i], *(init.begin() + i));
}
vec_size = init.size();
}
}迭代器构造函数
Vector(iterator begin, iterator end);
1
2
3
4
5
6
7
8
9
10// 迭代器构造函数实现
template <typename T>
Vector<T>::Vector(iterator begin, iterator end) : data(nullptr), vec_size(0), vec_capacity(0) {
size_t count = end - begin;
reserve(count);
for (size_t i = 0; i < count; ++i) {
std::allocator_traits<std::allocator<T>>::construct(allocator, &data[i], *(begin + i));
}
vec_size = count;
}
vector的析构函数
调用clear()
函数,清空allocator
分配的内存
1 | // 析构函数实现 |
vector添加元素的操作
push_back
:有两种方法,一种是拷贝构造,一种是移动构造1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19template <typename T>
void Vector<T>::push_back(const T& value) {//拷贝原对象
if (vec_size == vec_capacity) {
size_t new_capacity = (vec_capacity == 0) ? 1 : 2 * vec_capacity;
reserve(new_capacity);
}
std::allocator_traits<std::allocator<T>>::construct(allocator, data+vec_size, value);
++vec_size;
}
template <typename T>
void Vector<T>::push_back(T&& value) {//利用右值直接构造
if (vec_size == vec_capacity) {
size_t new_capacity = (vec_capacity == 0) ? 1 : 2 * vec_capacity;
reserve(new_capacity);
}
std::allocator_traits<std::allocator<T>>::construct(allocator, data+vec_size, std::move(value));
++vec_size;
}emplace_back
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 添加元素函数实现,直接构造
template <typename T>
template<typename... Args>
void Vector<T>::emplace_back(Args&&... args) {
if (vec_size == vec_capacity) {
size_t new_capacity = (vec_capacity == 0) ? 1 : 2 * vec_capacity;
reserve(new_capacity);
}
// 使用完美转发构造新元素
std::allocator_traits<std::allocator<T>>::construct(
allocator,
data + vec_size,
std::forward<Args>(args)...
);
++vec_size;
}insert
:利用迭代器实现了元素的定点插入1
2
3
4
5
6
7
8
9
10
11
12
13
14// 插入元素函数实现
template <typename T>
void Vector<T>::insert(iterator pos, const T& value) {
size_t index = pos - begin();
if (vec_size == vec_capacity) {
size_t new_capacity = (vec_capacity == 0) ? 1 : 2 * vec_capacity;
reserve(new_capacity);
}
for (size_t i = vec_size; i > index; --i) {
std::allocator_traits<std::allocator<T>>::construct(allocator, &data[i], std::move(data[i-1]));
}
std::allocator_traits<std::allocator<T>>::construct(allocator, &data[index], value);
++vec_size;
}
vector中排序的使用
这里使用了比较器模板,在 C++ 中,比较器模板允许你为算法(如排序、查找、优先级队列等)提供自定义的比较规则,同时保持代码的泛用性和高性能。以下是实现比较器模板的详细方法及示例:
比较器可以是 函数指针、函数对象(仿函数) 或 Lambda 表达式。它们需要满足以下条件:
- 接受两个相同类型的参数。
- 返回
bool
类型,表示两个元素的顺序关系。
这里在类中比较器模板的申明中,指定了Compare = std::less<T>
1 |
|
在C++20的标准下实现自己的vector容器