C++ vector的简单实现
作者:吃米饭 发布时间:2023-04-09 17:13:02
标签:实现,C++,vector
向量
向量是序列容器,表示可以更改大小的数组。
就像数组一样,向量对其元素使用连续的存储位置,这意味着也可以使用指向其元素的常规指针上的偏移量来访问其元素,并且与数组一样高效。但与数组不同的是,它们的大小可以动态变化,它们的存储由容器自动处理。
在内部,向量使用动态分配的数组来存储其元素。可能需要重新分配此数组,以便在插入新元素时增加大小,这意味着分配新数组并将所有元素移动到该数组。就处理时间而言,这是一项相对昂贵的任务,因此,每次将元素添加到容器时,向量都不会重新分配。
相反,向量容器可以分配一些额外的存储以适应可能的增长,因此容器的实际容量可能大于严格需要的存储来包含其元素(即其大小)。库可以实现不同的增长策略,以平衡内存使用和重新分配,但无论如何,重新分配应仅以对数增长的大小间隔发生,以便可以在向量末尾插入单个元素,并提供摊销的恒定时间复杂性。
因此,与数组相比,向量消耗更多的内存,以换取管理存储和以有效方式动态增长的能力。
与其他动态序列容器(deques、list 和 forward_lists)相比,向量非常有效地访问其元素(就像数组一样),并且相对有效地从其末尾添加或删除元素。对于涉及在末尾以外的位置插入或删除元素的操作,它们的性能比其他元素差,并且迭代器和引用的一致性低于 lists 和 forward_lists。
成员函数
(构造函数)构造 vector(公开成员函数)
(析构函数)析构 vector(公开成员函数)
operator=赋值给容器(公开成员函数)
assign将值赋给容器(公开成员函数)
get_allocator返回相关的分配器(公开成员函数)
元素访问
at访问指定的元素,同时进行越界检查(公开成员函数)
operator[]访问指定的元素(公开成员函数)
front访问第一个元素(公开成员函数)
back访问最后一个元素(公开成员函数)
data直接访问底层数组(公开成员函数)
迭代器
begin,cbegin(C++11)返回指向起始的迭代器(公开成员函数)
end,cend(C++11)返回指向末尾的迭代器(公开成员函数)
rbegin,crbegin(C++11)返回指向起始的逆向迭代器(公开成员函数)
rend,crend(C++11)返回指向末尾的逆向迭代器(公开成员函数)
容量
empty检查容器是否为空(公开成员函数)
size返回容纳的元素数(公开成员函数)
max_size返回可容纳的最大元素数(公开成员函数)
reserve预留存储空间(公开成员函数)
capacity返回当前存储空间能够容纳的元素数(公开成员函数)
shrink_to_fit(C++11)通过释放未使用的内存减少内存的使用(公开成员函数)
修改器
clear清除内容(公开成员函数)
insert插入元素(公开成员函数)
emplace(C++11)原位构造元素(公开成员函数)
erase擦除元素(公开成员函数)
push_back将元素添加到容器末尾(公开成员函数)
emplace_back(C++11)在容器末尾就地构造元素(公开成员函数)
pop_back移除末元素(公开成员函数)
resize改变容器中可存储元素的个数(公开成员函数)
swap交换内容(公开成员函数)
非成员函数
按照字典顺序比较 vector 中的值(函数模板)
operator==
operator!=(C++20 中移除)
operator<(C++20 中移除)
operator<=(C++20 中移除)
operator>(C++20 中移除)
operator>=(C++20 中移除)
operator<=>(C++20)
std::swap(std::vector)特化 std::swap 算法(函数模板)
erase(std::vector),erase_if(std::vector) (C++20)擦除所有满足特定判别标准的元素(函数模板
cpp
template <typename T>
class Vector
{
public:
Vector() noexcept = default;
explicit Vector(size_t n) : cap_{n}, ptr_{alloc(cap_)}
{
for (; len_ < n; ++len_)
{
construct(ptr_ + len_); //调用T的默认构造
}
}
Vector(size_t n, const T &x) : cap_{n}, ptr_{alloc(cap_)}
{
for (; len_ < n; ++len_)
{
construct(ptr_ + len_, x); //调用T的拷贝构造
}
}
Vector(const Vector &x) : cap_{x.size()}, ptr_{alloc(cap_)} //拷贝构造
{
for (; len_ < x.size(); ++len_)
{
construct(ptr_ + len_, x[len_]);
}
}
Vector(Vector &&x) noexcept //移动构造
{
cap_ = std::__exchange(x.cap_, 0);
len_ = std::__exchange(x.len_, 0);
ptr_ = std::__exchange(x.ptr_, nullptr);
}
Vector(std::initializer_list<T> li) : cap_{li.size()}, ptr_{alloc(cap_)} //初始化列表
{
for (auto &x : li)
{
construct(ptr_ + len_, x);
++len_;
}
}
~Vector() noexcept
{
clear();
dealloc(ptr_);
}
void swap(Vector &x) noexcept
{
using std::swap; // ADL
swap(cap_, x.cap_);
swap(len_, x.len_);
swap(ptr_, x.ptr_);
}
void clear() noexcept
{
for (; len_ > 0; --len_)
{
destroy(ptr_ + len_ - 1);
}
}
Vector &operator=(const T &x) //拷贝赋值
{
if (this != &x)
{
Vector{x}.swap(*this);
}
return *this;
}
Vector &operator=(T &&x) noexcept //移动赋值
{
if (this != &x)
{
Vector{std::move(x)}.swap(*this);
}
return *this;
}
Vector &operator=(std::initializer_list<T> li) //初始化列表赋值
{
Vector{li}.swap(*this);
return *this;
}
void push_back(const T &x) //拷贝
{
emplace_back(x);
}
void push_back(T &&x) //移动
{
emplace_back(x);
}
template <typename... Args>
void emplace_back(Args &&...args) //直接传递构造函数
{
if (len_ == cap_)
{
size_t new_cap = cap_ ? cap_ * 2 : 1; //等0返回1
T *new_ptr = alloc(new_cap);
for (size_t new_len; new_len < len_; ++new_len)
{
construct(new_ptr + new_len, std::move_if_noexcept(ptr_[new_len]));
}
cap_ = new_cap;
ptr_ = new_ptr;
}
construct(ptr_ + len_, std::forward<Args>(args)...);
++len_;
}
void pop_back() noexcept
{
if (len_ < cap_ / 2)
{
size_t new_cap = cap_ / 2;
T *new_ptr = alloc(new_cap);
for (size_t new_len = 0; new_len < len_; ++new_len)
{
construct(new_ptr + new_len, std::move_if_noexcept(ptr_[new_len]));
}
cap_ = new_cap;
ptr_ = new_ptr;
}
destroy(ptr_ + len_ - 1);
--len_;
}
size_t size() const noexcept
{
return len_;
}
size_t capacity() const noexcept
{
return cap_;
}
bool empty() const noexcept
{
return len_ == 0;
}
T &operator[](size_t i)
{
return ptr_[i];
}
const T &operator[](size_t i) const
{
return ptr_[i];
}
T *begin() noexcept
{
return ptr_;
}
T *end() noexcept
{
return ptr_ + len_;
}
const T *begin() const noexcept
{
return ptr_;
}
const T *end() const noexcept
{
return ptr_ + len_;
}
private:
T *alloc(size_t n) //分配n个大小内存
{
return static_cast<T *>(::operator new(sizeof(T) * n));
}
void dealloc(T *p) noexcept //释放内存
{
::operator delete(p);
}
template <typename... Args>
void construct(T *p, Args &&...args) //在这块内存上构造T类型对象
{
::new (p) T(std::forward<Args>(args)...);
}
void destroy(T *p) noexcept
{
p->~T();
}
private:
size_t cap_{0}; //容量
size_t len_{0}; //元素个数
T *ptr_{nullptr};
};
来源:https://blog.csdn.net/Cdreamfly/article/details/123315354


猜你喜欢
- 题目:将一个数组逆序输出。代码:import java.util.*;public class lianxi31 {public stati
- 本文实例讲述了Android开发中MotionEvent坐标获取方法。分享给大家供大家参考,具体如下:Android MotionEvent
- 前言前几天多名用户反馈同一个问题,在小新平板上无法上网课,点击上课按钮后就退回到首页了。同事了解了一下发现小新平板现在销量特别好,于是赶紧申
- 方法一class Program { [STAThread] static
- Android Studio Intent隐式启动,发短信,拨号,打电话,访问网页等实例代码功能创建5个按钮,隐式启动、发短信、拨号按钮、电
- 本文实例讲述了Java 8 Stream 的终极技巧——Collectors 功能与操作方法。分享给大家供大家参考,具体如下:1. 前言昨天
- 定义JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动
- 概念引入我们都知道,Java 创建的对象都是被分配到堆内存上,但是事实并不是这么绝对,通过对Java对象分配的过程分析,可以知道有两个地方会
- 哎,最近很好久没写点东西了,由于工作的原因,接触公司自己研发的底层orm框架,偶然发现该框架在调用jdbc操作的时候参考的是hibernat
- 本文介绍了Java中常见死锁与活锁的实例详解,分享给大家,具体如下:顺序死锁:过度加锁,导致由于执行顺序的原因,互相持有对方正在等待的锁资源
- 一、简介  Apache ShardingSphere 是一套开源的分布式数据库解决方案组成的生态圈,它
- 先对Spring SpringMVC和Mybatis单独进行配置,最后对三者进行整合配置Spring实际使用中,一般会使用注解+xml配置来
- 一、前期工作1.开启邮箱服务开启邮箱的POP3/SMTP服务(这里以qq邮箱为例,网易等都是一样的)2.导入依赖在springboot项目中
- 如:string str1 = "This is a test";string str2 = "This-is
- 参考链接亲测试以下版本成功激活附激活教程。idea下载链接(对应版本号下载):https://www.jetbrains.com/idea/
- 在学习Android开发的过程你,你往往会去借鉴别人的应用是怎么开发的,那些漂亮的动画和精致的布局可能会让你爱不释手,作为一个开发者,你可能
- 背景在我做WinForm开发的过程中,经常会遇到耗时操作或阻塞操作。他们会引发软件的卡顿甚至假死,严重影响软件的使用。因此,这类耗时或阻塞的
- 一,简介Feign使得 Java HTTP 客户端编写更方便。Feign 灵感来源于Retrofit、JAXRS-2.0和WebSocket
- 公司的研发管理平台实现了Gitlab+Kubernetes的Devops,在ToB和ToC场景中,由于用户量大,且预发布环境和生产环境或多或
- 以最终客户的角度来看,JAR文件就是一种封装,他们不需要知道jar文件中有多少个.class文件,每个文件中的功能与作用,同样可以得到他们希