7、【C++】STL容器:vector的用法及模拟实现

目录

一、vector概述

1. vector的定义与特性

vector是C++标准库提供的动态数组容器,定义在<vector>头文件中,属于序列式容器(元素按线性顺序排列)。它能够自动管理内存,根据元素数量动态调整存储空间大小,支持快速随机访问。

核心特性

  • 动态扩容:当元素数量超过当前容量时,自动分配更大的内存空间(通常为原容量的2倍),并拷贝元素。
  • 随机访问:通过下标(operator[])或迭代器可在O(1)时间内访问任意元素。
  • 连续存储:元素在内存中连续存放,与数组布局兼容,可通过指针偏移访问元素。
  • 尾部操作高效push_back(尾插)和pop_back(尾删)操作在大多数情况下为O(1)时间复杂度。

2. vector与数组的区别

特性vector数组(C-style array)
大小管理动态调整,无需手动分配/释放内存固定大小,编译期确定,不可更改
内存安全自动检查越界(部分实现)无越界检查,易导致缓冲区溢出
接口支持提供丰富成员函数(如push_back)仅支持基本指针操作,无内置接口
迭代器支持迭代器遍历需通过指针模拟迭代器功能
初始化支持多种初始化方式(列表初始化)仅支持静态初始化或手动赋值

3. vector的迭代器类型

vector提供四种迭代器类型,满足不同访问需求:

  • iterator:正向迭代器,可读写元素。
  • const_iterator:正向迭代器,只读元素(const对象使用)。
  • reverse_iterator:反向迭代器,可读写元素(从后向前遍历)。
  • const_reverse_iterator:反向迭代器,只读元素。

迭代器本质:vector的迭代器是原生指针的封装T*),因此支持指针的所有操作(++--+n-n等)。

示例

#include <vector>
#include <iostream>

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};
    
    // 正向迭代器(读写)
    std::vector<int>::iterator it = v.begin();
    for (; it != v.end(); ++it) {
        *it *= 2; // 修改元素
    }
    
    // const正向迭代器(只读)
    std::vector<int>::const_iterator cit = v.begin();
    for (; cit != v.end(); ++cit) {
        std::cout << *cit << " "; // 输出:2 4 6 8 10
    }
    
    // 反向迭代器
    std::vector<int>::reverse_iterator rit = v.rbegin();
    for (; rit != v.rend(); ++rit) {
        std::cout << *rit << " "; // 输出:10 8 6 4 2
    }
    return 0;
}

二、vector的基本用法

1. 构造函数与初始化

vector提供多种构造方式,适应不同初始化需求:

构造函数原型功能描述
vector()默认构造,创建空vector
vector(size_t n, const T& val = T())创建包含n个val的vector
vector(const vector& v)拷贝构造,复制v的内容
vector(InputIt first, InputIt last)范围构造,复制[first, last)区间的元素
vector(std::initializer_list<T> il)列表初始化(C++11),如vector<int> v{1,2,3}

示例

#include <vector>
#include <array>

int main() {
    // 默认构造
    std::vector<int> v1;
    
    // 包含5个3的vector
    std::vector<int> v2(5, 3); // {3,3,3,3,3}
    
    // 拷贝构造
    std::vector<int> v3(v2);
    
    // 范围构造(从数组拷贝)
    int arr[] = {1,2,3};
    std::vector<int> v4(arr, arr + 3); // {1,2,3}
    
    // 列表初始化(C++11)
    std::vector<int> v5{1,2,3,4}; // {1,2,3,4}
    
    return 0;
}

2. 元素访问

vector提供多种元素访问方式,兼顾效率和安全性:

  • operator[]:随机访问,无越界检查(高效)。
  • at(size_t pos):随机访问,越界时抛出std::out_of_range异常(安全)。
  • front():访问第一个元素。
  • back():访问最后一个元素。
  • data():返回指向底层数组的指针(C++11)。

示例

std::vector<int> v = {1,2,3,4,5};
std::cout << v[0] << std::endl;    // 1(无越界检查)
std::cout << v.at(2) << std::endl; // 3(有越界检查)
std::cout << v.front() << std::endl; // 1
std::cout << v.back() << std::endl;  // 5
int* p = v.data(); // 指向v[0]的指针

3. 容量与大小管理

vector的容量(capacity)和大小(size)是两个核心概念:

  • size:当前元素个数。
  • capacity:当前分配的内存可容纳的最大元素个数(不重新分配内存的情况下)。

常用接口

  • size():返回size。
  • capacity():返回capacity。
  • empty():判断是否为空(size == 0)。
  • reserve(size_t new_cap):调整capacity至new_cap(仅增大,不改变size)。
  • resize(size_t n, T val = T()):调整size至n,新增元素用val填充。
  • shrink_to_fit():将capacity缩减至size(C++11,释放未使用内存)。

示例

std::vector<int> v;
v.reserve(10); // capacity=10,size=0
v.resize(5, 1); // size=5,capacity=10,元素为{1,1,1,1,1}
v.push_back(2); // size=6,capacity=10
v.shrink_to_fit(); // capacity=6(释放多余内存)

4. 修改操作

vector提供丰富的元素修改接口:

  • push_back(const T& val):尾插元素。
  • pop_back():尾删元素。
  • insert(iterator pos, const T& val):在pos位置插入val。
  • erase(iterator pos):删除pos位置元素。
  • swap(vector& v):交换两个vector的内容(O(1)时间)。
  • clear():清空所有元素(size=0,capacity不变)。

示例

std::vector<int> v = {1,2,3};
v.push_back(4); // {1,2,3,4}
v.pop_back();   // {1,2,3}
auto it = v.begin() + 1;
v.insert(it, 5); // {1,5,2,3}
v.erase(it);     // {1,2,3}(it指向5,删除后it失效)
v.clear();       // size=0,capacity=4(假设初始capacity为4)

三、vector迭代器失效问题

1. 迭代器失效的场景

迭代器本质是指向vector底层数组的指针,当数组内存发生变化或元素位置改变时,迭代器会失效(指向无效内存)。常见失效场景:

(1)扩容导致失效

push_backreserve触发扩容时,vector会分配新内存并拷贝元素,原迭代器指向旧内存,导致失效。

std::vector<int> v = {1,2,3};
int* p = &v[0]; // 指向旧内存
v.reserve(100); // 触发扩容,分配新内存
std::cout << *p << std::endl; // 未定义行为(旧内存已释放)
(2)插入元素导致失效
  • 尾部插入(未扩容):迭代器有效(end()失效)。
  • 中间插入:插入位置之后的迭代器失效(元素后移导致位置改变)。
std::vector<int> v = {1,2,3,4};
auto it = v.begin() + 2; // 指向3
v.insert(it, 5); // 插入后元素为{1,2,5,3,4}
// it现在指向5的下一个位置(原3的位置),但插入后it已失效
(3)删除元素导致失效
  • 尾部删除:仅end()失效,其他迭代器有效。
  • 中间删除:删除位置之后的迭代器失效(元素前移导致位置改变)。
std::vector<int> v = {1,2,3,4};
auto it = v.begin() + 1; // 指向2
v.erase(it); // 删除后元素为{1,3,4},it失效(指向原2的位置,现为3)

2. Visual Studio和g++对迭代器失效的表现差异

不同编译器对迭代器失效的处理策略不同,导致表现差异:

(1)Visual Studio(MSVC)
  • 迭代器校验:Debug模式下,迭代器包含额外校验信息(如版本号),失效后访问会直接崩溃(抛出std::out_of_range或断言失败)。
  • 示例
    std::vector<int> v = {1,2,3};
    auto it = v.begin();
    v.push_back(4); // 若触发扩容,it失效
    *it = 5; // Debug模式下崩溃:迭代器已失效
    
(2)G++(GNU libstdc++)
  • 无校验机制:迭代器本质是裸指针,失效后访问不会立即崩溃,但行为未定义(可能修改错误内存或程序异常)。
  • 示例
    std::vector<int> v = {1,2,3};
    auto it = v.begin();
    v.push_back(4); // 若未触发扩容,it仍有效;若扩容,it指向旧内存
    *it = 5; // 未扩容时正常,扩容后修改无效内存(未定义行为)
    

3. 解决迭代器失效的方法

(1)使用成员函数返回值更新迭代器

inserterase返回新的有效迭代器,可用于更新:

std::vector<int> v = {1,2,3,4};
auto it = v.begin() + 1; // 指向2

// insert返回新插入元素的迭代器
it = v.insert(it, 5); // it现在指向5
++it; // 指向2(原位置元素)

// erase返回删除元素的下一个迭代器
it = v.erase(it); // 删除2,it指向3
(2)避免在遍历中修改容量

遍历前reserve足够容量,避免扩容:

std::vector<int> v;
v.reserve(100); // 预分配容量
for (int i = 0; i < 100; ++i) {
    v.push_back(i); // 无扩容,迭代器有效
}
(3)使用索引而非迭代器

遍历中使用索引访问,不受迭代器失效影响:

std::vector<int> v = {1,2,3,4};
for (size_t i = 0; i < v.size(); ++i) {
    if (v[i] == 2) {
        v.erase(v.begin() + i); // 删除后索引自动调整
        --i; // 修正索引(元素前移)
    }
}
(4)使用范围for时避免修改容器

范围for基于迭代器实现,修改容器(如插入/删除)会导致迭代器失效:

// 错误示例:范围for中删除元素
for (auto x : v) {
    if (x == 2) v.erase(...); // 迭代器失效,范围for内部迭代器未更新
}

// 正确示例:使用传统for循环并更新迭代器
for (auto it = v.begin(); it != v.end();) {
    if (*it == 2) {
        it = v.erase(it); // 更新迭代器
    } else {
        ++it;
    }
}

四、模拟实现构造函数调用不明确问题

1. 问题描述

C++11引入初始化器列表(initializer_list)后,当vector构造函数同时支持大小初始化列表初始化时,可能导致调用歧义。

场景

// 模拟vector构造函数
template <typename T>
class vector {
public:
    // 构造函数1:n个val
    vector(size_t n, const T& val = T()) {
        std::cout << "vector(size_t, const T&)" << std::endl;
    }
    
    // 构造函数2:初始化器列表
    vector(std::initializer_list<T> il) {
        std::cout << "vector(initializer_list<T>)" << std::endl;
    }
};

int main() {
    vector<int> v(10, 20); // 调用构造函数1(10个20)
    vector<int> v2{10, 20}; // 调用构造函数2(列表初始化,元素{10,20})
    
    // 歧义场景:当参数为(size_t, int)时
    vector<int> v3(10, 20); // 明确调用构造函数1
    vector<int> v4{10, 20}; // 明确调用构造函数2
    
    // 问题:当参数为(10)时,是构造10个默认元素还是列表初始化{10}?
    vector<int> v5(10); // 调用构造函数1(10个int())
    vector<int> v6{10}; // 调用构造函数2(列表初始化{10})
    return 0;
}

歧义根源:当构造函数参数既能匹配size_t又能匹配initializer_list时,编译器优先选择初始化器列表构造函数(C++11规则)。

2. 解决调用不明确的方法

(1)使用explicit关键字

将单参数构造函数声明为explicit,禁止隐式类型转换,避免编译器误判:

template <typename T>
class vector {
public:
    // 单参数构造函数声明为explicit
    explicit vector(size_t n, const T& val = T()) { ... }
    vector(std::initializer_list<T> il) { ... }
};

// 此时,列表初始化只能调用initializer_list构造函数
vector<int> v{10}; // 明确调用initializer_list版本
vector<int> v2(10); // 明确调用size_t版本
(2)使用std::initializer_list强制列表初始化

通过std::initializer_list显式指定列表初始化,消除歧义:

vector<int> v(std::initializer_list<int>{10, 20}); // 明确调用列表构造函数
(3)避免模糊参数类型

调用构造函数时,确保参数类型匹配预期构造函数:

// 传递size_t参数(避免int隐式转换为size_t导致歧义)
vector<int> v(static_cast<size_t>(10), 20); // 明确匹配(size_t, T)构造函数

五、reserve中的深浅拷贝问题

1. reserve中浅拷贝发生原因

当vector存储自定义类型对象(尤其是包含指针成员的对象)时,reserve触发扩容会执行浅拷贝(逐字节拷贝对象),导致多个对象共享同一块内存,析构时重复释放,引发崩溃。

示例场景

class String {
public:
    char* _str;
    String(const char* str = "") {
        _str = new char[strlen(str) + 1];
        strcpy(_str, str);
    }
    ~String() { delete[] _str; } // 析构时释放_str
};

int main() {
    std::vector<String> v;
    v.reserve(2); // 初始容量2
    v.push_back(String("hello")); // 插入第一个元素
    v.reserve(10); // 触发扩容,浅拷贝元素
    // 此时v[0]._str与原临时对象的_str指向同一块内存
    // 析构时临时对象先释放_str,v[0]._str成为野指针,再次释放崩溃
}

2. 浅拷贝发生的图解

扩容前内存布局

vector对象: [ ptr -------> 堆内存: [String("hello")] (size=1, capacity=2)
                                  | _str -------> "hello\0"

扩容后浅拷贝内存布局

旧内存(待释放): [String("hello")]
                 | _str -------> "hello\0"

vector对象: [ ptr -------> 新堆内存: [String("hello")] (size=1, capacity=10)
                                  | _str -------> "hello\0"(与旧内存共享)

析构时崩溃:旧内存的String对象析构释放_str,新内存的String对象析构时再次释放已失效的_str,导致双重释放错误。

3. 解决方法

(1)重写拷贝构造函数和赋值运算符(深拷贝)

为自定义类型添加深拷贝构造和赋值运算符,确保拷贝时复制指针指向的内容而非指针本身:

class String {
public:
    char* _str;
    String(const char* str = "") {
        _str = new char[strlen(str) + 1];
        strcpy(_str, str);
    }
    
    // 深拷贝构造函数
    String(const String& s) {
        _str = new char[strlen(s._str) + 1];
        strcpy(_str, s._str);
    }
    
    // 深拷贝赋值运算符
    String& operator=(const String& s) {
        if (this != &s) {
            delete[] _str;
            _str = new char[strlen(s._str) + 1];
            strcpy(_str, s._str);
        }
        return *this;
    }
    
    ~String() { delete[] _str; }
};
(2)使用智能指针(如std::shared_ptr

通过智能指针管理内存,自动处理引用计数,避免手动释放:

#include <memory>

class String {
public:
    std::shared_ptr<char> _str; // 共享指针,自动管理引用计数
    String(const char* str = "") {
        _str = std::shared_ptr<char>(new char[strlen(str) + 1], [](char* p) { delete[] p; });
        strcpy(_str.get(), str);
    }
    // 无需手动编写拷贝构造和赋值运算符(shared_ptr自动处理)
};
(3)避免在vector中存储原始指针

优先存储值类型对象或智能指针,减少手动内存管理:

// 推荐:存储值类型
std::vector<std::string> v1; // std::string内部实现深拷贝

// 推荐:存储智能指针
std::vector<std::shared_ptr<int>> v2;

// 不推荐:存储原始指针
std::vector<char*> v3; // 需手动管理指针生命周期,易导致浅拷贝问题

六、模拟实现vector的核心接口

1. 成员变量设计

vector的核心成员变量包括指向数据的指针、大小和容量:

template <typename T>
class vector {
public:
    typedef T* iterator;
    typedef const T* const_iterator;

private:
    iterator _start;      // 指向第一个元素
    iterator _finish;     // 指向最后一个元素的下一个位置
    iterator _end_of_storage; // 指向容量末尾
};

关系

  • size() = _finish - _start
  • capacity() = _end_of_storage - _start

2. 构造函数与析构函数

(1)默认构造函数
vector() : _start(nullptr), _finish(nullptr), _end_of_storage(nullptr) {}
(2)带大小和初始值的构造函数
vector(size_t n, const T& val = T()) {
    reserve(n); // 预分配容量
    for (size_t i = 0; i < n; ++i) {
        push_back(val); // 尾插元素
    }
}
(3)初始化器列表构造函数(C++11)
vector(std::initializer_list<T> il) {
    reserve(il.size());
    for (const T& val : il) {
        push_back(val);
    }
}
(4)拷贝构造函数(深拷贝)
vector(const vector& v) {
    reserve(v.capacity());
    for (const T& val : v) {
        push_back(val);
    }
}
(5)析构函数
~vector() {
    if (_start) {
        // 销毁所有元素(调用T的析构函数)
        for (iterator it = _start; it != _finish; ++it) {
            it->~T();
        }
        // 释放内存
        operator delete(_start);
        _start = _finish = _end_of_storage = nullptr;
    }
}

3. 迭代器实现

iterator begin() { return _start; }
const_iterator begin() const { return _start; }
iterator end() { return _finish; }
const_iterator end() const { return _finish; }

reverse_iterator rbegin() { return reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }

4. 容量管理接口

(1)reserve
void reserve(size_t new_cap) {
    if (new_cap > capacity()) {
        size_t old_size = size();
        // 分配新内存(operator new分配原始内存,不调用构造函数)
        iterator new_start = (iterator)operator new(new_cap * sizeof(T));
        iterator new_finish = new_start;
        
        // 拷贝旧元素到新内存(调用T的拷贝构造函数)
        for (iterator it = _start; it != _finish; ++it) {
            new (new_finish) T(*it); // 定位new:在new_finish处构造T
            ++new_finish;
        }
        
        // 销毁旧元素并释放旧内存
        if (_start) {
            for (iterator it = _start; it != _finish; ++it) {
                it->~T();
            }
            operator delete(_start);
        }
        
        // 更新指针
        _start = new_start;
        _finish = new_finish;
        _end_of_storage = _start + new_cap;
    }
}
(2)resize
void resize(size_t n, const T& val = T()) {
    if (n > size()) {
        // 插入新元素
        reserve(n);
        for (size_t i = size(); i < n; ++i) {
            push_back(val);
        }
    } else if (n < size()) {
        // 销毁多余元素
        while (_finish != _start + n) {
            --_finish;
            _finish->~T();
        }
    }
}

5. 元素访问接口

T& operator[](size_t pos) {
    assert(pos < size());
    return _start[pos];
}

const T& operator[](size_t pos) const {
    assert(pos < size());
    return _start[pos];
}

T& front() {
    assert(!empty());
    return *_start;
}

const T& front() const {
    assert(!empty());
    return *_start;
}

T& back() {
    assert(!empty());
    return *(_finish - 1);
}

const T& back() const {
    assert(!empty());
    return *(_finish - 1);
}

6. 修改操作接口

(1)push_back
void push_back(const T& val) {
    if (_finish == _end_of_storage) {
        // 扩容:新容量=旧容量*2(至少1)
        size_t new_cap = capacity() == 0 ? 1 : capacity() * 2;
        reserve(new_cap);
    }
    new (_finish) T(val); // 定位new:在_finish处构造元素
    ++_finish;
}
(2)pop_back
void pop_back() {
    assert(!empty());
    --_finish;
    _finish->~T(); // 销毁最后一个元素
}
(3)insert
iterator insert(iterator pos, const T& val) {
    assert(pos >= _start && pos <= _finish);
    size_t len = pos - _start;
    if (_finish == _end_of_storage) {
        size_t new_cap = capacity() == 0 ? 1 : capacity() * 2;
        reserve(new_cap);
    }
    // 更新pos(扩容后可能失效,需重新计算)
    pos = _start + len;
    // 移动元素(从后向前)
    for (iterator it = _finish; it != pos; --it) {
        new (it) T(*(it - 1)); // 拷贝构造前一个元素
        (it - 1)->~T(); // 销毁前一个元素
    }
    new (pos) T(val); // 插入新元素
    ++_finish;
    return pos; // 返回新插入元素的迭代器
}
(4)erase
iterator erase(iterator pos) {
    assert(pos >= _start && pos < _finish);
    // 移动元素(从前向后覆盖)
    for (iterator it = pos + 1; it != _finish; ++it) {
        *(it - 1) = *it; // 赋值运算符(需T支持)
    }
    --_finish;
    _finish->~T(); // 销毁最后一个元素
    return pos; // 返回删除元素的下一个迭代器
}

七、模拟实现的vector整体代码

以下是完整的vector模拟实现代码,包含上述所有接口:

#include <cstddef>
#include <initializer_list>
#include <algorithm>
#include <cassert>

namespace my {
template <typename T>
class vector {
public:
    // 迭代器类型
    typedef T* iterator;
    typedef const T* const_iterator;
    typedef std::reverse_iterator<iterator> reverse_iterator;
    typedef std::reverse_iterator<const_iterator> const_reverse_iterator;

    // 构造函数
    vector() : _start(nullptr), _finish(nullptr), _end_of_storage(nullptr) {}

    vector(size_t n, const T& val = T()) {
        reserve(n);
        for (size_t i = 0; i < n; ++i) {
            push_back(val);
        }
    }

    vector(std::initializer_list<T> il) {
        reserve(il.size());
        for (const T& val : il) {
            push_back(val);
        }
    }

    vector(const vector& v) {
        reserve(v.capacity());
        for (const T& val : v) {
            push_back(val);
        }
    }

    // 赋值运算符
    vector& operator=(const vector& v) {
        if (this != &v) {
            vector tmp(v); // 拷贝构造临时对象
            swap(tmp); // 交换临时对象和当前对象
        }
        return *this;
    }

    // 析构函数
    ~vector() {
        if (_start) {
            // 销毁所有元素
            for (iterator it = _start; it != _finish; ++it) {
                it->~T();
            }
            // 释放内存
            operator delete(_start);
            _start = _finish = _end_of_storage = nullptr;
        }
    }

    // 迭代器接口
    iterator begin() { return _start; }
    const_iterator begin() const { return _start; }
    iterator end() { return _finish; }
    const_iterator end() const { return _finish; }

    reverse_iterator rbegin() { return reverse_iterator(end()); }
    const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
    reverse_iterator rend() { return reverse_iterator(begin()); }
    const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }

    // 容量接口
    size_t size() const { return _finish - _start; }
    size_t capacity() const { return _end_of_storage - _start; }
    bool empty() const { return size() == 0; }

    void reserve(size_t new_cap) {
        if (new_cap > capacity()) {
            size_t old_size = size();
            iterator new_start = (iterator)operator new(new_cap * sizeof(T));
            iterator new_finish = new_start;

            // 拷贝旧元素到新内存
            for (iterator it = _start; it != _finish; ++it) {
                new (new_finish) T(*it);
                ++new_finish;
            }

            // 销毁旧元素并释放内存
            if (_start) {
                for (iterator it = _start; it != _finish; ++it) {
                    it->~T();
                }
                operator delete(_start);
            }

            // 更新指针
            _start = new_start;
            _finish = new_finish;
            _end_of_storage = _start + new_cap;
        }
    }

    void resize(size_t n, const T& val = T()) {
        if (n > size()) {
            reserve(n);
            for (size_t i = size(); i < n; ++i) {
                push_back(val);
            }
        } else if (n < size()) {
            while (_finish != _start + n) {
                --_finish;
                _finish->~T();
            }
        }
    }

    void shrink_to_fit() {
        if (capacity() > size()) {
            reserve(size()); // 缩容到size
        }
    }

    // 元素访问接口
    T& operator[](size_t pos) {
        assert(pos < size());
        return _start[pos];
    }

    const T& operator[](size_t pos) const {
        assert(pos < size());
        return _start[pos];
    }

    T& at(size_t pos) {
        if (pos >= size()) {
            throw std::out_of_range("vector::at");
        }
        return _start[pos];
    }

    const T& at(size_t pos) const {
        if (pos >= size()) {
            throw std::out_of_range("vector::at");
        }
        return _start[pos];
    }

    T& front() {
        assert(!empty());
        return *_start;
    }

    const T& front() const {
        assert(!empty());
        return *_start;
    }

    T& back() {
        assert(!empty());
        return *(_finish - 1);
    }

    const T& back() const {
        assert(!empty());
        return *(_finish - 1);
    }

    // 修改操作接口
    void push_back(const T& val) {
        if (_finish == _end_of_storage) {
            size_t new_cap = capacity() == 0 ? 1 : capacity() * 2;
            reserve(new_cap);
        }
        new (_finish) T(val);
        ++_finish;
    }

    void pop_back() {
        assert(!empty());
        --_finish;
        _finish->~T();
    }

    iterator insert(iterator pos, const T& val) {
        assert(pos >= _start && pos <= _finish);
        size_t len = pos - _start;
        if (_finish == _end_of_storage) {
            size_t new_cap = capacity() == 0 ? 1 : capacity() * 2;
            reserve(new_cap);
        }
        pos = _start + len; // 重新计算pos(扩容后可能失效)
        // 移动元素
        for (iterator it = _finish; it != pos; --it) {
            new (it) T(*(it - 1));
            (it - 1)->~T();
        }
        new (pos) T(val);
        ++_finish;
        return pos;
    }

    iterator erase(iterator pos) {
        assert(pos >= _start && pos < _finish);
        for (iterator it = pos + 1; it != _finish; ++it) {
            *(it - 1) = *it;
        }
        --_finish;
        _finish->~T();
        return pos;
    }

    void swap(vector& v) {
        std::swap(_start, v._start);
        std::swap(_finish, v._finish);
        std::swap(_end_of_storage, v._end_of_storage);
    }

    void clear() {
        for (iterator it = _start; it != _finish; ++it) {
            it->~T();
        }
        _finish = _start;
    }

private:
    iterator _start;         // 指向第一个元素
    iterator _finish;        // 指向最后一个元素的下一个位置
    iterator _end_of_storage;// 指向容量末尾
};
} // namespace my

// 使用示例
#include <iostream>

int main() {
    my::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.insert(v.begin() + 1, 4); // {1,4,2,3}
    for (int x : v) {
        std::cout << x << " ";
    }
    v.erase(v.begin() + 2); // {1,4,3}
    std::cout << std::endl;
    for (int x : v) {
        std::cout << x << " ";
    }
    return 0;
}

八、vector使用注意事项与最佳实践

1. 避免频繁扩容

  • 预分配容量:通过reserve预先分配已知的最大容量,减少扩容次数(如读取文件前reserve文件大小)。
  • 选择合适的初始容量:避免初始容量过小导致频繁扩容(如vector v(0); v.reserve(1000);)。

2. 迭代器失效处理

  • 使用返回值更新迭代器inserterase返回新迭代器,及时更新避免失效。
  • 范围for中禁止修改容器:范围for内部迭代器无法更新,修改容器会导致失效。

3. 存储自定义类型时的内存管理

  • 确保深拷贝:自定义类型需重写拷贝构造和赋值运算符,避免浅拷贝问题。
  • 优先使用智能指针:存储指针时,使用std::shared_ptrstd::unique_ptr管理生命周期。

4. 性能优化

  • 使用emplace_back替代push_back:C++11的emplace_back直接在容器中构造元素,避免临时对象拷贝(需编译器支持)。
  • 避免不必要的拷贝:传递vector时使用引用(const vector&),避免值传递导致的深拷贝。

5. 安全性考虑

  • 使用at()而非operator[]at()提供越界检查,适合调试阶段;operator[]效率更高,适合发布版本。
  • 清空容器后释放内存:使用clear()后调用shrink_to_fit()释放未使用内存(C++11)。

通过以上实践,可高效、安全地使用vector,充分发挥其动态数组的优势。

更多推荐