我们将实现一个简化版的 std::vector
,这个容器能够动态地存储任何类型的元素,并支持在元素数量增加时自动扩容。通过模板,我们能做到类型无关,也就是说,无论是存储 int
、double
,还是自定义类型 Test
的对象,都能使用这个容器。
#include <iostream>
#include <stdexcept> // 用于异常处理
// 定义一个模板类 SimpleVector,支持任何类型的元素
template <typename T>
class SimpleVector {
private:
T* data; // 数据存储的原始指针
size_t size; // 当前容器的元素个数
size_t capacity; // 当前容器分配的容量
// 扩容函数:当容器满时自动扩展容量
void resize(size_t new_capacity) {
// 创建一个新的内存区域,大小为 new_capacity
T* new_data = new T[new_capacity];
// 将原有的数据拷贝到新的内存区域
for (size_t i = 0; i < size; ++i) {
new_data[i] = data[i]; // 拷贝每个元素
}
// 释放原先的内存
delete[] data;
// 更新指针和容量
data = new_data;
capacity = new_capacity;
}
public:
// 构造函数:初始化容量为0
SimpleVector() : data(nullptr), size(0), capacity(0) {}
// 析构函数:在对象销毁时释放内存
~SimpleVector() {
delete[] data; // 自动释放内存
}
// 添加元素到容器末尾
void push_back(const T& value) {
// 如果当前元素数达到了容量,则扩容
if (size == capacity) {
resize(capacity == 0 ? 1 : capacity * 2); // 扩展容量:0时初始化为1,否则翻倍
}
// 将新元素放入容器末尾
data[size++] = value;
}
// 获取指定索引的元素
T& operator[](size_t index) {
// 检查索引是否合法
if (index >= size) {
throw std::out_of_range("Index out of bounds"); // 超出边界抛出异常
}
return data[index];
}
// 获取当前容器中元素的个数
size_t get_size() const {
return size;
}
// 判断容器是否为空
bool is_empty() const {
return size == 0;
}
};
class Test {
public:
Test() {
std::cout << "Test object created." << std::endl;
}
~Test() {
std::cout << "Test object destroyed." << std::endl;
}
void hello() {
std::cout << "Hello, I'm a Test object!" << std::endl;
}
};
int main() {
SimpleVector<int> vec; // 创建一个 SimpleVector 容器,存储整数类型的元素
vec.push_back(10); // 添加元素 10
vec.push_back(20); // 添加元素 20
vec.push_back(30); // 添加元素 30
std::cout << "Size of vector: " << vec.get_size() << std::endl; // 输出容器的当前大小
// 输出容器中的所有元素
for (size_t i = 0; i < vec.get_size(); ++i) {
std::cout << "Element " << i << ": " << vec[i] << std::endl;
}
std::cout << "End of scope." << std::endl; // 程序结束时会自动释放内存
return 0;
}
1. SimpleVector
类模板
我们使用了模板类 SimpleVector
,通过 template <typename T>
声明,允许 SimpleVector
管理任何类型的数据。T
是我们传递给模板的类型,它可以是 int
、double
,甚至是自定义类型。
私有成员:
T* data
: 存储元素的指针,实际上是动态分配的一块内存区域。size_t size
: 当前容器中实际存储的元素数量。size_t capacity
: 容器的总容量,即分配的内存空间大小。
resize()
方法:
当我们向 SimpleVector
添加新元素时,如果当前容量不足,它会调用 resize()
扩展内存:
- 内存扩展:我们新分配一块更大的内存,并将原有的数据复制到新区域。
- 内存释放:原来的内存空间通过
delete[]
释放,防止内存泄漏。
push_back()
方法:
我们用 push_back()
向容器中添加元素。每次添加元素时,如果当前容量已满,它会调用 resize()
方法自动扩容。
2. 析构函数 ~SimpleVector()
:
当 SimpleVector
对象销毁时,析构函数会被自动调用,它负责释放 data
指向的内存区域。因为我们使用的是动态分配的内存,所以必须显式地进行释放,防止内存泄漏。
3. 访问元素:
我们通过重载 operator[]
来让用户像使用普通数组一样访问容器中的元素。为了避免越界访问,我们添加了一个边界检查。如果访问的索引超出了当前元素的范围,会抛出 std::out_of_range
异常。
4. 主函数 main()
:
- 我们创建了一个存储
int
类型的SimpleVector
容器,向其中添加了三个元素:10、20 和 30。 vec.push_back(10)
会将10
添加到容器的末尾,依此类推,直到添加完所有元素。- 使用
vec.get_size()
获取容器的大小。 - 最后,使用循环访问容器中的每个元素,并输出。
内存管理:
- 自动扩容:当容器满时,我们自动扩展内存。
resize()
方法确保新元素能顺利添加,而不需要用户手动管理内存。 - 内存释放:析构函数
~SimpleVector()
会在容器对象销毁时释放所有动态分配的内存,避免了内存泄漏。
优势与扩展:
- 通用性:通过模板,我们的容器不仅限于
int
类型,还可以管理任何类型的数据。 - 动态扩容:容器会根据需要自动扩展大小,避免了数组大小固定的限制。
- 内存安全:使用
delete[]
确保释放动态分配的内存,避免了内存泄漏。 - 功能扩展:你可以继续扩展
SimpleVector
,添加更多功能,比如删除元素、清空容器、支持迭代器等。