在double类型vector的基础上实现vector的模版类
问题:
传入模版的类型不是内建类型的情况—c++萃取机制
- POD(Plain Old Data)类型,指的是C++的内建数据类型,还有原生指针和C风格的结构体联合体等。标准的定义是:能用C的memcpy()等函数进行操作 的类、结构体或联合体。POD类型有以下标准:
- 没有用户自定义的构造函数、析构函数、拷贝赋值运算符;
- 没有虚函数和虚基类;
- 所有非静态成员都是public;
- 所有非静态成员都是POD类型;
- 没有继承或只继承了POD类型。
- POD类型和复杂数据类型最大的区别是四个方面:构造(默认构造器)、析构、赋值、复制(拷贝构造器),即default_constructor,destructor,assignment_operator,copy_constructor。 复杂数据类型先天缺少这四样(编译器给它加上的不算),而可以认为POD类型是先天具备或者 根本不用考虑 这四样。
constexpr关键字
-
noexcept
是 C++11 中引入的一个关键字,用于指定函数是否可能抛出异常。在函数声明或定义时,可以使用noexcept
关键字来指定该函数是否可能抛出异常,以便更好地管理和处理异常情况。具体来说,
noexcept
关键字有以下几种用法:noexcept
表示函数不会抛出任何异常。
例如:
void foo() noexcept { // 函数体不会抛出异常 }
noexcept(true)
表示函数不会抛出任何异常。
例如:
void foo() noexcept(true) { // 函数体不会抛出异常 }
noexcept(false)
表示函数可能会抛出异常。
例如:
void foo() noexcept(false) { // 函数体可能会抛出异常 }
noexcept(expression)
表示函数的异常性取决于expression
中的表达式。
例如:
void foo() noexcept(sizeof(int) == 4) { // 函数体可能会抛出异常,取决于 sizeof(int) 是否等于 4 }
使用
noexcept
关键字可以帮助编写更加健壮和可靠的代码,特别是在处理异常和错误情况时,可以更加精确地控制异常的传递和处理。同时,使用noexcept
关键字还可以提高代码的执行效率,因为编译器可以根据函数是否可能抛出异常来进行优化。
new/operator new/placement new
[参考](https://blog.csdn.net/finewind/article/details/82631906)
start = new T[n];
实际上完成了三个操作:new
表达式调用operatar new[]
的标准库函数。该函数分配一块足够大的、原始的、未命名的内存空间以便存储特定类型的对象的数组- 编译运行相应的构造函数一构造这些对象,并为其传入初始值
- 对象被分配了空间并构造完成,返回一个指向该对象的指针
operator new
主要作用是分配内存空间,无法使用construct函数构造对象。原型:void *operator new(size_t);
分配一个对象void *operator new[](size_t);
分配一个数组- 分配对象后要对返回的
void*
进行强制类型转化- 如
T* p = (T*)::operator new(n*sizeof(T));
- 如
- 释放调用delete释放
void *operator delete(void*) noexcept;
void *operator delete[](void*)noexcept;
- 使用
noexcept
关键字可以更加精确地控制异常的传递和处理,避免不必要的异常捕获和处理开销。 - 使用
noexcept
关键字可以提高代码的执行效率,因为编译器可以根据函数是否可能抛出异常来进行优化。
- 使用
placement new
定位new,在一个特定的、预先分配的内存地址上构造对象。原型:new(place_address) type
new(place_address type (initializers))
new(place_address) type [size]
new(place_address) type [size] {braced initializer list}
- 用
placement new
对象的初始化和销毁以及内存的释放new(start) T(value);
start->~T();
delete start;
数组的内存释放delete[] start;
思路:
template <typename T>
class vector
{
protected:
// 用T* 类型表示vector的头尾和可用空间的最大值
T *start; // 表示当前空间的头
T *finish; // 表示当前使用空间的尾
T *end_of_storage; // 表示当前可用空间的尾
// 辅助函数,作用:用于vector空间不够时扩容
// 可用空间为0时,申请更大的空间,并把元素都复制过去
void allocate_and_copy()
{
// std::cout << "调用allocate_and_copy" << std::endl;
int len = (capacity() != 0 ? capacity() * 2 : 1); // 以容量的2倍为规则扩容
T *temp = new T[len]; // 创建更大的空间,temp指向可用空间的头
for (int i = 0; i < size(); ++i)
{
temp[i] = start[i];
}
end_of_storage = temp + len;
finish = temp + size();
// delete start; // 回收旧数组的头节点,实际上整个旧数组都被释放了
delete[] start; // 回收旧数组
start = temp;
}
public:
// 构造函数:这里用成员初始化列表进行初始化
vector() : start(nullptr), finish(nullptr), end_of_storage(nullptr)
{ // 默认构造
std::cout << "vector的默认构造" << std::endl;
}
// 有参构造:初始化vector的长度为n
vector(int n)
{
std::cout << "vector的有参构造vector(n)" << std::endl;
start = new T[n]; // 先创建空间
finish = start;
end_of_storage = start + n;
for (int i = 0; i < n; ++i) // 初始值设为0.0
{
start[i] = 0.0;
}
}
// 有参构造:初始化vector的长度为n,并将这些位置上的数据全部赋值为value
vector(int n, const T &value)
{
std::cout << "vector的有参构造vector(n,value)" << std::endl;
start = new T[n]; // 先创建空间
finish = start + n; // 因为对数组的数组是从0开始存放的,start+n表示的是使用空间的下一个位置
end_of_storage = start + n;
for (int i = 0; i < n; ++i) // 初始值设为0
{
start[i] = value;
}
}
// 拷贝构造
vector(const vector &another)
{
std::cout << "vector的拷贝构造函数" << std::endl;
// 创建一个新空间存放another的数据
int len = another.size();
start = new T[len];
for (int i = 0; i < len; ++i)
{
// start[i] = *(another.begin() + i); //const对象不能调用非const成员函数
start[i] = another[i];
}
finish = start + len;
end_of_storage = finish;
}
// 析构函数
~vector()
{
std::cout << "vector的析构函数" << std::endl;
delete[] start;
}
// 常用接口
/**********************首尾******************************/
// 给出vcetor当前使用空间的起始位置
T *begin() const
{
return start;
}
// 给出vector当前使用空间的下一个位置
T *end() const
{
return finish;
}
// 逆序遍历时,使用空间的起始位置
T *rbegin()
{
return end() - 1;
}
// 逆序遍历时,使用空间的结束位置
T *rend()
{
return begin() - 1;
}
/**********************随机访问******************************/
// vector中第一个数据
T &front()
{
return *begin(); // 尽量避免直接使用和暴露成员变量,保证类的封装性
}
// vector中最后一个数据
T &back()
{
return *(end() - 1);
}
T &operator[](int n) // 返回引用,便于修改
{ // 不考虑n越界的情况
return *(begin() + n);
}
T &operator[](int n) const // 返回引用,便于修改
{ // 不考虑n越界的情况
return *(begin() + n);
}
/**********************大小******************************/
// 此处const的作用:
// 一、将成员函数声明为const,表明该函数不会对对象进行修改
// 二、可以允许在const对象上调用该函数
// vector中当前有多少个数据
int size() const
{
return finish - start;
}
// 容量:vector中当前最多能存放多少个数据
int capacity() const
{
return int(end_of_storage - begin());
}
int max_size() const
{
return end_of_storage - start;
}
// vector当前是否为空
bool empty() const
{
return begin() == end();
}
/********************** 增删改 ******************************/
// 在vector的末尾插入一个数据
void push_back(const T &x)
{
// 容器内空间不够,就扩容
if (finish == end_of_storage)
{ // 如果长度为零,初始空间设为1
std::cout << "capacity()的值为:" << capacity() << std::endl;
allocate_and_copy();
}
*finish = x;
//*end() = x; //ERROR:因为end()是静态成员函数,不能修改对象
++finish;
}
// 在vector的末尾删除一个数据
void pop_back()
{
--finish; // ERROR:因为end()是静态成员函数,不能修改对象
}
// 删除position所指向的数据
void erase(T *position)
{
// 把指针后面的全部前移
for (T *i = position; (i + 1) != end(); ++i)
{
*i = *(i + 1);
}
--finish;
}
// 删除从first到last这一段数据
void erase(T *first, T *last)
{
int diff = last - first;
int count = 0; // 计数器
for (T *i = last; i != end(); ++i, ++count) // 如果last等于end(),就不用移动后面的数据了,直接更新finish就行
{
*(first + count) = *(first + diff + count); // 这里等号右边必须是first + diff,刚好是last的下一个位置,计数器从0开始遍历正好
}
finish = finish - diff; // 指针向前移动
}
// 调整vetor的容量,如果调整后的容量大于调整前,则用数据x填充空余部分;
// 如果调整后容量小于调整前,则只保留前new_size位数据
void resize(int new_size, const T &x)
{
if (new_size < size())
{
erase(begin() + new_size, end());
}
else
{
std::cout << "capacity()的值为:" << capacity() << std::endl;
// 考虑需要扩容的情况
while (new_size > capacity())
{ // 数组是从0开始存储的,所以如果new_size = capacity()的情况刚好能存放
allocate_and_copy();
std::cout << "capacity()的值为:" << capacity() << std::endl;
}
for (int i = 0; i < new_size - size(); ++i)
{
*(finish + i) = x;
}
finish += new_size - size();
}
}
// 调整后的容量大于调整前,则用默认数据填充空余部分,其余上同
void resize(int new_size)
{
resize(new_size, 0.0);
}
// 清空vector中所有数据
void clear()
{
erase(begin(), end());
}
};
vector类的全部源码
template<class T, class Alloc=alloc> //alloc直接调用第二级空间配置器,当要分配的内存大于128kb时,调用第一级配置器
class vector {
public:
/** vector的嵌套类型定义 **/
typedef T value_type;
typedef value_type *pointer;
typedef value_type *iterator; //定义vector类的迭代器
typedef const value_type *const_iterator;
typedef value_type &reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef const value_type *const_pointer;
typedef const value_type &const_reference;
protected:
/** 设置空间配置器 **/
//这个是空间配置器的API
typedef simple_alloc<value_type, Alloc> data_allocator; //确定了分配空间的类型
//定义迭代器
iterator start; //表示目前使用的空间的头
iterator finish; //表示目前使用的空间的尾
iterator end_of_storage; //表示目前可用空间的尾
//配置空间并填满内容
iterator allocate_and_copy(const_iterator first, const_iterator last, iterator result) {
size_type len = size_type(last - first);
result = data_allocator::allocate(len);
uninitialized_copy(first, last, result);
return result;
}
iterator allocate_and_fill(size_type n, const_reference x) {
iterator result = data_allocator::allocate(n); //先分配空间,已知分配空间的类型,只需传递要分配的个数即可
uninitialized_fill_n(result, n, x);
return result;
}
void insert_aux(iterator position, const_reference x);
//销毁容器所占的空间
void deallocate() {
if (start) {
data_allocator::deallocate(start, end_of_storage - start);
}
}
void fill_initialize(size_type n, const_reference value) {
start = allocate_and_fill(n, value);
finish = start + n;
end_of_storage = finish;
}
void copy_initialize(const vector<T, Alloc> &value) {
start = allocate_and_copy(value.begin(), value.end(), start);
finish = start + value.size();
end_of_storage = finish;
}
public:
/** 构造 **/
//默认构造
vector() : start(nullptr), finish(nullptr), end_of_storage(nullptr) {
}
//有参构造,会调用T类型的默认构造
explicit vector(size_type n) { //explicit是一个C++关键字,用于修饰构造函数,指示编译器不要对 参数 自动执行隐式类型转换
fill_initialize(n, value_type());
}
vector(size_type n, const_reference value) {
fill_initialize(n, value);
}
vector(int n, const_reference value) {
fill_initialize(n, value);
}
vector(long n, const_reference value) {
fill_initialize(n, value);
}
vector(const value_type *first, const value_type *last) {
start = allocate_and_copy(first, last, start);
finish = start + (last - first);
end_of_storage = finish;
}
/** 拷贝构造 **/
vector(const vector<T, Alloc> &value) {
copy_initialize(value);
}
/** 析构 **/
~vector() {
destroy(start, finish);
deallocate(); //释放空间
}
/** 常用接口 **/
/** 首尾 **/
iterator begin() { return start; }
const_iterator begin() const { return start; }
iterator end() { return finish; }
const_iterator end() const { return finish; }
reference front() { return *begin(); }
const_reference front() const { return *begin(); }
reference back() { return *(end() - 1); }
const_reference back() const { return *(end() - 1); }
/** 大小 **/
//容量
size_type capacity() const { return end_of_storage - begin(); }
size_type size() const { return size_type(end() - begin()); }
bool empty() const { return begin() == end(); }
/** 增删改查 **/
//随机访问
reference operator[](size_type n) {
return *(begin() + n);
}
const_reference operator[](size_type n) const {
return *(begin() + n);
}
//增
//从position开始,插入n个元素
//返回的是新插入元素的第一个
iterator insert(iterator position, const_reference x) {
size_type n = position - begin();
if (finish != end_of_storage && position == end()) {
construct(finish, x);
++finish;
} else {
insert_aux(position, x);
}
//return position; //当position是end()可能出错
return begin() + n;
}
iterator insert(iterator position, size_type n, const_reference x);
void insert(iterator position, const_iterator first, const_iterator last);
void push_back(const_reference value) {
if (finish != end_of_storage) {
construct(finish, value);
++finish;
} else {
insert_aux(end(), value);
}
}
//删
void pop_back() {
--finish;
destroy(finish);
}
//返回指向被删除元素之后的第一个元素的迭代器,方便在删除一个或多个元素后,遍历容器中的剩余元素
iterator erase(iterator position) { //清除某个位置上的元素
if (position + 1 != end()) {
//如果position指的不是最后一个对象,需要把后面的全部向前移动一个位置
uninitialized_copy(position + 1, finish, position);
}
--finish;
destroy(finish);
return position;
}
//这里的移除是左闭右开
iterator erase(iterator first, iterator last) {
//这里要不要考虑last越界的问题
//这个复制是左闭右开的
iterator temp = uninitialized_copy(last, finish, first);
destroy(temp, finish);
finish = finish - (last - first);
return first;
}
void resize(size_type new_size, const_reference x) {
if (new_size < size()) {
erase(begin() + new_size, end());
} else
insert(end(), new_size - size(), x);
}
void resize(size_type new_size) {
resize(new_size, value_type());
}
void clear() {
erase(begin(), end());
}
};
template<class T, class Alloc>
void vector<T, Alloc>::insert_aux(iterator position, const_reference x) {
if (finish != end_of_storage) { //这是随机插入用的
construct(finish, *(finish - 1));
++finish;
T x_copy = x;
copy_backward(position, finish - 2, finish - 1);
*position = x_copy;
} else { //这个是push_back用的
const size_type old_size = size();
const size_type len = old_size != 0 ? old_size * 2 : 1;
iterator new_start = data_allocator::allocate(len);
iterator new_finish = new_start;
//把旧容器的对象都复制到新分配的空间
try {
new_finish = uninitialized_copy(start, position, new_start);
construct(new_finish, x); //把对象x插入新分配的空间
++new_finish;
new_finish = uninitialized_copy(position, finish, new_finish); //这行代码在push_back中没用,应该是在insert中使用的
}
catch (...) { //错误处理
destroy(new_start, new_finish);
data_allocator::deallocate(new_start, len);
throw;
}
//析构并释放原vector
destroy(begin(), end());
deallocate();
//调整迭代器,指向新vector
start = new_start;
finish = new_finish;
end_of_storage = new_start + len;
}
}
//从position开始,插入n个元素x
template<class T, class Alloc>
typename vector<T, Alloc>::iterator vector<T, Alloc>::insert(iterator position, size_type n, const_reference x) {
if (n != 0) { //先判断插入元素个数的合法性
if (size_type(capacity() - size()) >= n) { //再判断当然剩余空间是否满足n个元素的空间需求
value_type x_copy = x;
const size_type elems_after = end() - position;
// const size_type elems_after = distance(position, end());
iterator old_finish = finish;
if (elems_after > n) { //插入点之后的现有元素个数大于新增元素个数
// uninitialized_copy(position, finish, position + n); 不能这么搞,在从前往后复制的过程中会覆盖掉原来的元素导致出错
uninitialized_copy(finish - n, finish, finish); //把finish之前的n个元素移动到finish之后
finish = old_finish + n; //更新finish
copy_backward(position, old_finish - n,
old_finish); //再把position到old_finish-n范围内的元素从后往前依次复制到old_finish范围内
fill(position, position + n, x_copy); //把填充到position,positon +n范围内,
/*这样做的好处是移动现有元素的过程中不会相互覆盖*/
} else {
//插入点之后的现有元素个数小于等于先增元素个数
uninitialized_fill_n(finish, n - elems_after, x_copy); //先把大于elems的个数插入到finish之后
finish = finish + n - elems_after; //更新finish
uninitialized_copy(position, old_finish,
finish); //再把position,finish范围内的元素填充到容器尾部
finish += elems_after; //更新finish
uninitialized_fill(position, old_finish, x_copy); //最后把x填充到position,old-finish中
}
} else {
//备用空间小于"新增元素个数",需要配置额外的内存
//首先决定新长度:旧长度的两倍或者旧长度+新增元素的个数
const size_type old_size = size();
const size_type len = old_size + max(old_size, n);
iterator new_start = data_allocator::allocate(len);
iterator new_finish = new_start;
try {
//以下首先将旧vector的插入点之前的元素复制到新空间
new_finish = uninitialized_copy(start, position, new_start);
//把要插入的元素插入新分配的空间
new_finish = uninitialized_fill_n(new_finish, n, x);
//把插入点之后的元素插入新空间
new_finish = uninitialized_copy(position, finish, new_finish);
}
//#ifdef __STL_USE_EXCEPTIONS
catch (...) {
//如果有异常发生,就销毁新分配的空间并报错
destroy(new_start, new_finish);
data_allocator::deallocate(new_start, len);
throw;
}
//#endif
//以下是清除并释放旧的vector
destroy(start, finish);
deallocate();
start = new_start;
finish = new_finish;
end_of_storage = new_finish + len;
return position;
}
}
}
template<class T, class Alloc>
void vector<T, Alloc>::insert(iterator position, const_iterator first, const_iterator last) {
if (first != last) {
size_type n = 0;
n = distance(first, last); //在stl_iterator.h中
if (size_type(end_of_storage - finish) >= n) {
const size_type elems_after = finish - position;
iterator old_finish;
if (elems_after > n) {
uninitialized_copy(finish - n, finish, finish);
finish = finish + n;
copy_backward(position, old_finish - n, old_finish);
copy(first, last, position);
} else {
uninitialized_copy(first + elems_after, last, finish);
finish = finish + n - elems_after;
uninitialized_copy(position, old_finish, finish);
finish += elems_after;
copy(first, first + n, position);
}
} else {
const size_type old_size = size();
const size_type len = old_size + max(old_size, n);
iterator new_start = data_allocator::allocate(len);
iterator new_finish = new_start;
try {
new_finish = uninitialized_copy(start, position, new_start);
new_finish = uninitialized_copy(first, last, new_finish);
new_finish = uninitialized_copy(position, finish, new_finish);
}
catch (...) {
destroy(new_start, new_finish);
data_allocator::deallocate(new_start, len);
throw;
}
destroy(start, finish);
deallocate();
start = new_start;
finish = new_finish;
end_of_storage = start + len;
}
}
}