如何实现一个自定义的STL容器类,需要注意哪些方面
·
一、实现时需要注意的关键方面
1. 模板与类型定义
模板类应该是一个类模板:template<typename T>
提供标准类型定义:value_type, iterator, const_iterator 等,用于与 STL 算法兼容。
2. 内存管理
使用 new / delete 或 allocator<T> 管理内存。
避免内存泄漏:确保所有内存都被正确释放(尤其是在异常处理时)。
实现 Rule of Five(拷贝构造、拷贝赋值、移动构造、移动赋值、析构)。
3. 异常安全性
所有操作(如 push_back)必须提供强异常保证或基本异常保证。
std::allocator 提供构造、销毁元素的方法,可用于安全构造对象。
4. 迭代器兼容性
提供 begin()、end() 返回迭代器。
迭代器应至少支持 STL 输入迭代器(可以直接用裸指针 T* 作为迭代器)。
5. 与 STL 算法兼容
类型别名如 value_type, reference, const_reference 可用于 STL 算法模板匹配。
重载操作符(如 operator[])与标准容器行为一致。
6. 性能优化
push_back 和 reallocate 时应该采用几何扩展策略(例如容量每次乘以 2)。
使用 std::move 移动元素,减少复制开销。
注:
value_type 表示“容器里元素的类型”。
std::vector<int> t;
std::vector<int>::value_type x = 42;
🔄 它的作用是?
主要是为 模板泛型算法 和 容器无关的编程 提供一种统一访问元素类型的方式。
例子:写一个函数模板接受任何容器
template<typename Container>
void printFirst(const Container& c) {
typename Container::value_type val = *c.begin();
std::cout << val << std::endl;
}
无论传入的是 std::vector<int> 还是 std::list<std::string>,value_type 都能正确推导出元素类型。
reference
reference 表示“容器中元素的引用类型”,通常是 T&,用于读写元素。
template <typename Container>
void setFirstElement(Container& c, const typename Container::value_type& newValue) {
typename Container::reference ref = c.front(); // T& 类型
ref = newValue; // 修改第一个元素
}
二、实现代码:
template<typename T>
class MyVector {
public:
// 类型定义
using value_type = T;
using iterator = T*;
using const_iterator = const T*;
// 构造函数、析构函数
MyVector();
MyVector(size_t size);
~MyVector();
// 拷贝/移动 构造与赋值运算符
MyVector(const MyVector& other);
MyVector& operator=(const MyVector& other);
MyVector(MyVector&& other) noexcept;
MyVector& operator=(MyVector&& other) noexcept;
// 容器接口
void push_back(const T& value);
void pop_back();
T& operator[](size_t index);
const T& operator[](size_t index) const;
size_t size() const;
bool empty() const;
// 迭代器支持
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
private:
T* data_;
size_t size_;
size_t capacity_;
void reallocate(size_t new_capacity);
};
MyVector(const MyVector& other) 和MyVector(MyVector&& other) noexcept有什么区别,other不都是传进来的引用吗,都是不拷贝一份?
const MyVector& other只是不拷贝一份形参,但实现内容会深拷贝一份返回给新对象,且不能修改原值 MyVector&&
other 是可以修改原值的 因此它可以将原值修改为空,并且接管原值指向的内存
#include <cstddef> // for size_t
#include <stdexcept> // for exceptions
#include <utility> // for std::move
#include <algorithm> // for std::copy
template<typename T>
class MyVector {
public:
using value_type = T;
using iterator = T*;
using const_iterator = const T*;
// 默认构造函数
MyVector()
: data_(nullptr), size_(0), capacity_(0) {}
// 有参数构造函数
MyVector(size_t size)
: data_(new T[size]), size_(size), capacity_(size) {}
// 析构函数
~MyVector() {
delete[] data_;
}
// 拷贝构造函数(深拷贝)
MyVector(const MyVector& other)
: data_(new T[other.capacity_]), size_(other.size_), capacity_(other.capacity_) {
std::copy(other.data_, other.data_ + other.size_, data_);
}
// 拷贝赋值运算符(深拷贝)
MyVector& operator=(const MyVector& other) {
if (this != &other) {
delete[] data_;
data_ = new T[other.capacity_];
size_ = other.size_;
capacity_ = other.capacity_;
std::copy(other.data_, other.data_ + other.size_, data_);
}
return *this;
}
// 移动构造函数
MyVector(MyVector&& other) noexcept
: data_(other.data_), size_(other.size_), capacity_(other.capacity_) {
other.data_ = nullptr;
other.size_ = 0;
other.capacity_ = 0;
}
// 移动赋值运算符
MyVector& operator=(MyVector&& other) noexcept {
if (this != &other) {
delete[] data_;
data_ = other.data_;
size_ = other.size_;
capacity_ = other.capacity_;
other.data_ = nullptr;
other.size_ = 0;
other.capacity_ = 0;
}
return *this;
}
// 添加元素
void push_back(const T& value) {
if (size_ >= capacity_) {
reallocate(capacity_ == 0 ? 1 : capacity_ * 2);
}
try {
data_[size_] = value;
++size_;
} catch (...) {
// 如果赋值失败,size_ 不增加
throw;
}
}
// 删除最后一个元素
void pop_back() {
if (size_ == 0) throw std::out_of_range("Vector is empty");
--size_;
}
// 下标访问
T& operator[](size_t index) {
if (index >= size_) throw std::out_of_range("Index out of range");
return data_[index];
}
const T& operator[](size_t index) const {
if (index >= size_) throw std::out_of_range("Index out of range");
return data_[index];
}
// 当前元素数量
size_t size() const {
return size_;
}
// 是否为空
bool empty() const {
return size_ == 0;
}
// 迭代器
iterator begin() { return data_; }
iterator end() { return data_ + size_; }
const_iterator begin() const { return data_; }
const_iterator end() const { return data_ + size_; }
private:
T* data_;
size_t size_;
size_t capacity_;
// 内部扩容函数
void reallocate(size_t new_capacity) {
// 先分配新内存
T* new_data = new T[new_capacity];
size_t i = 0;
try {
// 尝试移动构造所有已有元素
for (; i < size_; ++i) {
new_data[i] = std::move(data_[i]);
}
} catch (...) {
// 如果中途抛异常,需要销毁已构造对象并释放内存
delete[] new_data;
throw; // 重新抛出异常
}
// 一切安全完成后再替换旧数据
delete[] data_;
data_ = new_data;
capacity_ = new_capacity;
}
};
更多推荐



所有评论(0)