STL-vector的实现

想实现自己的一个STL。先写vector。

vector实际上是一个泛型变长数组。基本的数组操作都是很直观的,难点在于变长和对泛型的支持。


变长的支持:

变长,简单来说,就是在向一个vector对象中增加元素时,vector会随着元素的增加而动态扩容。

因此,用C语言中的malloc和free便可实现vector的变长与析构。但这只能实现对内置类型的支持。若要对泛型元素进行支持,C语义的malloc与free无法进行显示的构造函数和析构函数的调用。这时便要用到C++的内存分配机制。

C++的内存分配的风格一般是new和delete,然而我们知道,如果要在每次向vector对象增加元素时,便重新申请一个新的长度的数组,再将原来的元素copy过去,这样的开销是不可承受的。所以vector的内存分配策略往往是这样:当元素个数为N时,数组的长度为L=2^K,其中K为非负整数,使得2^(K-1) < N <= 2^K。这时就需要一个机制,可以1.分配或释放未构造(即未调用构造函数,或已调用了析构函数)的内存;2.在给定的内存区域进行构造(调用构造函数)与析构(调用析构函数)。

C++提供了两种方法,这两种方法均可独立地支持上述机制的两个功能,且可以混合操作。即上述可用第一种方法实现功能1,而用第二种方法实现功能2。反之亦然。

1.使用allocator类,allocator类提供了allocate(n)与deallocate(p,n)这两个函数来分配和回收未构造的内存,提供了construct(p, t)与destroy(p)这两个函数来对一块未构造的内存进行构造,或对已构造的内存进行析构。

这种方式的优点在于类型安全,因为allocator类是类模板,在使用时需要指定模板参数(即分配或销毁的类),这样在分配内存时,只需指出分配的对象的数目即可。

缺点在于构造内存时只能用复制构造函数,不够灵活。(只能用复制构造函数,连默认的构造函数也无法使用哦~)

2. 使用operator new与operator delete进行分配和释放未构造的内存。用定位new表达式对未构造内存进行初始化,用显示调用对象的析构函数来撤销对象。

这种方法的优点在于使用定位new表达式时可以自由使用任意构造函数。缺点在于operator new在申请内存时需要明确内存的具体大小(即字节数),容易出错。


无论采用何种方法,均有有uninitialized_xxxx()函数族来使用迭代器对连续的内存进行批量构造(也是使用复制构造函数)。


下面阐述后来被证伪:

由于使用allocator类时,对每一个vector类模板的实现,都有一个静态的allocator对象,导致了一种很不好的结果。阐述如下:

因为vector是类模板,所以其声明与实现需要在同一个文件中(编译需要,我也不是很清楚为什么),这个文件的后缀我将它命名为.hpp。但一旦该类模板中出现的静态成员,由于前述约束,该静态成员也必须在这个文件中进行初始化。这就导致了,如果有多个.cpp的源文件要使用vector,包含了vector.hpp时,上述静态成员便在多处进行了初始化,导致编译错误。

在网上也查到了几个解决方法,但有的无法实现,有的约束过多。

故这里统一采用operator new与operator delete的方法来申请和释放未构造的内存。


后来发现,多文件包含后并没有产生静态变量多处定义的错误。错误原因为:当多文件包含vector.hpp时,由于main.cpp在包含vector.hpp之前包含了iostream,而另一个cpp文件只包含了vector.hpp,而在vector.hpp中使用的std::length_error。这就导致了另一个cpp文件包含的vector.hpp文件中没有std::length_error的定义,导致语法分析出错。

但仍然使用operator new与operator delete,并结合uninitialized_xxxx函数族进行内存管理。


对泛型的支持:

最直观地,加上模板参数即可。但问题在于对迭代器的支持。

待续

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STL中的vector是一个动态数组,可以在运行时动态增加或减少元素。 vector实现可以分为以下几个部分: 1. 数据存储:vector的元素存储在连续的内存空间中,可以使用指针或者数组实现。 2. 大小和容量的管理:vector需要维护当前元素的数量,以及已经分配的内存大小和容量。当元素数量达到容量时,需要重新分配内存空间。 3. 迭代器:vector需要支持迭代器,以便可以通过迭代器访问元素。 下面是一个简单的vector实现: ```c++ template<typename T> class vector { public: // 构造函数 vector() : m_data(nullptr), m_size(0), m_capacity(0) {} // 析构函数 ~vector() { if (m_data) { delete[] m_data; } } // 在末尾添加一个元素 void push_back(const T& val) { // 如果空间不够,需要重新分配内存 if (m_size == m_capacity) { int new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; T* new_data = new T[new_capacity]; for (int i = 0; i < m_size; ++i) { new_data[i] = m_data[i]; } if (m_data) { delete[] m_data; } m_data = new_data; m_capacity = new_capacity; } // 在末尾添加元素 m_data[m_size++] = val; } // 返回元素数量 int size() const { return m_size; } // 返回已分配的内存大小 int capacity() const { return m_capacity; } // 返回第i个元素 T& operator[](int i) { return m_data[i]; } const T& operator[](int i) const { return m_data[i]; } // 迭代器 typedef T* iterator; iterator begin() { return m_data; } iterator end() { return m_data + m_size; } private: T* m_data; int m_size; int m_capacity; }; ``` 这个实现使用了指针来存储元素,管理大小和容量,以及实现迭代器。当元素数量达到容量时,会重新分配内存空间。此外,这个实现还支持迭代器,允许用户通过迭代器访问元素。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值