专业编程基础技术教程

网站首页 > 基础教程 正文

c++ 疑难杂症(4) std:vector c++ vector subscript out of range

ccvgpt 2024-11-11 11:24:32 基础教程 7 ℃

std::vector的使用频率比较高, 有必要盘盘它。

0.概念

std::vector 是 C++ 标准库中的一个动态数组模板类。它允许您存储和操作一系列连续的元素。以下是关于 std::vector 的几个关键点:

c++ 疑难杂症(4) std:vector c++ vector subscript out of range

  1. 动态大小:与常规数组不同,std::vector 的大小可以在运行时改变。您可以使用 push_back(), pop_back(), insert(), erase(), 和其他成员函数来更改向量的大小。
  2. 连续存储std::vector 保证其元素在内存中是连续存储的,这使得访问和修改元素非常快速。
  3. 容量增长:当您向 std::vector 添加元素时,它会自动重新分配内存以容纳更多的元素,通常会按比例增长其容量。
  4. 迭代器std::vector 提供了随机访问迭代器,这使得它可以像常规数组一样进行索引,但同时也支持许多 STL(标准模板库)算法。
  5. 内存管理std::vector 负责自己的内存管理,它会自动调整其大小以适应元素的需要,并在必要时重新分配内存。
  6. 类型安全:由于 std::vector 是模板类,因此它可以存储任何类型的元素,并且类型安全。
  7. 效率:虽然 std::vector 需要维护内部指针和数据结构,但大多数操作(如添加或删除元素)的开销都相对较低。对于许多应用,使用 std::vector 比使用常规数组或手动管理内存更高效。

1.示例

代替字符数组

#include<iostream>
#include <vector>
#include <memory>
void main() {
    //模拟代替字符数组 char buf[4096];
    std::vector<char> buf(4096);
    strcpy(buf.data(), "hello world");
    std::cout << strlen(buf.data()) << std::endl;
    std::cout << buf.data() << std::endl;
    //当然现在智能指针使用替换
    //std::unique_ptr<char[]> ptr(new char[4096]);
}

常规操作

#include<iostream>
#include <vector>
#include <algorithm> 
int main() {
    
    std::vector<std::string> vecS({"one", "two"});

    //添加
    vecS.push_back("three");
    vecS.emplace_back("four");
    vecS.insert(vecS.begin(), "zero");
    vecS.insert(vecS.end(), { "five", "six" });
    vecS.emplace(vecS.end(), "seven");
    vecS.clear();//全部删除
    vecS.assign(7, "ok");//分配7个串, 每个都是"ok"
    vecS.assign({"zero", "one", "two", "three", "four", "five", "size", "seven"});

    //访问
    if (0 == vecS[2].compare("two")) {
        std::cout << vecS[2] << std::endl;
        //打印输出: two
    }
    
    //遍历
    for (auto elem : vecS) {
        //for (std::string elem : vecS) {
        std::cout << elem << " ";
    }
    std::cout << std::endl;
    //打印输出: zero one two three four five six seven

    for (auto& elem : vecS) {
  //for (std::string& elem : vecS) {
  //for (const auto& elem : vecS) {
  //for (const std::string& elem : vecS) {
        std::cout << elem << " ";
    }
    std::cout << std::endl;
    //打印输出: zero one two three four five six seven

    std::for_each(vecS.begin(), vecS.end(), 
        [](std::string& elem) { std::cout << elem << " "; });
    std::cout << std::endl;
    //打印输出: zero one two three four five six seven

    //查找
    std::vector<std::string>::iterator it;
    it = std::find(vecS.begin(), vecS.end(), "three");
    it = std::find_if(vecS.begin(), vecS.end(), 
        [](std::string& elem) { return 0 == elem.compare("three"); });

    //排序  (先打乱, 然后再0-7排序)
    std::sort(vecS.begin(), vecS.end());//默认的,打乱了0-7排序
    std::sort(vecS.begin(), vecS.end(), 
              [](const std::string& x, const std::string& y) {
        //只是演示,不考虑效率
        int x1 = 0, y1 = 0;
        std::string cmp[] = { "zero", "one", "two", "three", "four", "five", "size", "seven" };
        for (int i = 0; i < sizeof(cmp) / sizeof(cmp[0]); i++) {
            if (x1 == 0 && 0 == cmp[i].compare(x)) {
                x1 = i;
            }
            if (y1 == 0 && 0 == cmp[i].compare(y)) {
                y1 = i;
            }
        }
        return x1 < y1;
    });
    
    //删除
    vecS.pop_back();//删除最后一个"five"
    vecS.erase(vecS.begin());//删除首个"zero"
    vecS.erase(std::find(vecS.begin(), vecS.end(), "three"));
    //遍历删除
    for (auto iter = vecS.begin(); iter != vecS.end(); ++iter) {

        if (0 == iter->compare("four")) {
            vecS.erase(iter--);//注意,删除时倒退
            continue;
        }
        std::cout << *iter <<" ";
    }
    std::cout << std::endl;
    //打印输出: one two five six

    //复制
    std::vector<std::string> vecCopy(vecS);
    vecCopy.clear();
    vecCopy.insert(vecCopy.begin(), vecS.begin(), vecS.end());
    vecCopy.clear();
    vecCopy = vecS;
    //vecCopy.operator=(vecS);
    vecCopy.clear();
    vecCopy.assign(vecS.begin(), vecS.end());
    if (vecCopy == vecS) {
        std::cout << "same" << std::endl;
    }
    return 0;
}
/*打印输出:
two
one two three four five size seven
one two three four five size seven
one two three four five size seven
two five size
same
*/

移动语义支持

#include<iostream>
#include <vector>
#include <algorithm> 

int main() {

    class A {
        int x = 0;
    public:
        A(int _x) : x(_x) {
            std::cout << "A() : x=" << x << std::endl;
        }
        A(const A& a) {//拷贝构造函数
            x = a.x;
            std::cout << "A(const A& a) : x=" << x << std::endl;
        }
        A(A&& a) noexcept { //移动构造函数
            x = a.x;
            a.x = 0;
            std::cout << "A(A&& a) : x=" << x << std::endl;
        }
        ~A() {
            std::cout << "~A() : x=" << x << std::endl;
        }
        A& operator=(const A& a) { //拷贝赋值运算符
            if (this != &a) {
                x = a.x;
            }
            std::cout << "operator=(const A& a) : x=" << x << std::endl;
            return *this;
        }
        A& operator=(A&& a) noexcept { //移动赋值运算符
            if (this != &a) {
                x = a.x;
                a.x = 0;
            }
            std::cout << "operator=(const A& a) : x=" << x << std::endl;
            return *this;
        }
        void show() { std::cout << "x=" << x << std::endl; }
    };

    std::vector<A> vec;
    //添加成员方法基本上都添加了移动语义支持
    //void push_back(_Ty&& _Val)
    //iterator insert(const_iterator _Where, _Ty&& _Val)
    //decltype(auto) emplace_back(_Valty&&... _Val)
    //iterator emplace(const_iterator _Where, _Valty&&... _Val)

  //下面举例说明:

    {//移动语义
        A a(1);
        vec.push_back(std::move(a));
        vec.push_back(A(2));
        //void push_back(_Ty&& _Val)
    }
    {//移动语义
        vec.emplace_back(A(3));
        vec.emplace_back(4);
        //decltype(auto) emplace_back(_Valty&&... _Val)
    }
    {//移动语义
        A a(5);
        vec.insert(vec.end(), std::move(a));
        vec.insert(vec.end(), A(6));
        //iterator insert(const_iterator _Where, _Ty&& _Val)
    }
    {
        A a(7);
        vec.emplace(vec.end(), a);
        vec.emplace(vec.end(), 8);
        //iterator emplace(const_iterator _Where, _Valty&&... _Val)
    }
    {//通过移动转移
        vec[vec.size() - 1].show(); //打印: 8
        A a = std::move(vec[vec.size() - 1]);
        vec[vec.size() - 1].show();//打印: 0
        vec.pop_back();//转移之后,无效了,应该移除
    }
    {//通过移动转移
        std::vector<A> vec1(std::move(vec));
        std::vector<A> vec2 = std::move(vec1);
        vec = std::move(vec2);
    }
    {//通过移动转移
        //auto func = [](std::vector<A>&& a)->std::vector<A>&& { return std::forward<std::vector<A>>(a); };
        auto func = [](std::vector<A>&& a) { return std::forward<std::vector<A>>(a); };
        auto vec1 = func(std::forward<std::vector<A>>(vec));
        vec = std::move(vec1);
     }
    return 0;
}

解析: 这段C++代码定义了一个名为A的类,该类有一个私有成员变量x,以及一些公共成员函数,如构造函数、拷贝构造函数、移动构造函数、析构函数、拷贝赋值运算符和移动赋值运算符。 演示std::vector的成员方法中关于移动语义的支持。移动语义是一种优化技术,它可以减少对象拷贝的开销,提高程序的性能。在C++11中引入了右值引用,使得移动语义成为可能。

首先,代码定义了一个名为A的类,该类有一个成员方法show(),用于打印对象的值。

然后,代码创建了一个std::vector<A>类型的变量vec,并使用push_back()方法向其中添加了几个A类型的对象。

接下来,代码演示了四种不同的方法来向vec中添加元素:

使用push_back()方法,并将参数作为右值传递。这种方法会自动调用A类的移动构造函数,从而实现移动语义。

使用emplace_back()方法,并将参数作为右值传递。这种方法会自动调用A类的移动构造函数,从而实现移动语义。

使用insert()方法,并将参数作为右值传递。这种方法会自动调用A类的移动构造函数,从而实现移动语义。

使用emplace()方法,并将参数作为右值传递。这种方法会自动调用A类的移动构造函数,从而实现移动语义。

最后,代码演示了如何通过移动语义将对象从一个vector转移到另一个vector中。

注意:在实际编程中,为了更好地利用移动语义,可以尽量使用右值引用作为函数参数,并在需要时使用std::forward()进行类型转换。

与智能指针结合

#include<iostream>
#include <vector>
#include <memory>
int main() {

    class A {
        int x = 0;
    public:
        A(int _x) : x(_x) {
            std::cout << "A() : x=" << x << std::endl;
        }
        void show() { std::cout << "x=" << x << std::endl; }
    };

    {//vector 存放 shared_ptr
        auto func = [](A* a) {
            std::cout << "delete" << std::endl;
            delete a;
            };

        std::vector<std::shared_ptr<A>> vec;
        vec.push_back(std::shared_ptr<A>(new A(1), func));
        vec.push_back(std::make_shared<A>(2));
        vec.emplace(vec.end(), new A(3));
        vec.emplace_back(new A(4));
        vec.insert(vec.end(), { std::make_shared<A>(5), std::shared_ptr<A>(new A(6), func) });

        while (!vec.empty()) {
            vec.pop_back();
        }
    }

    {//shared_ptr 包裹 std::vector
        auto func = [](std::vector<A>* ptr) {
            std::cout << "delete vector" << std::endl;
            ptr->clear();
            delete ptr;
            };
        std::shared_ptr<std::vector<A>> ptr = std::shared_ptr<std::vector<A>>(new std::vector<A>(), func);
        ptr.reset();
    }
    return 0;
}
/*
第一个代码块:在这个代码块中,
我们创建一个 std::vector<std::shared_ptr<A>> 并向其中添加元素。
定义一个 lambda 函数 func,它接受一个指向 A 的指针,并删除它。
创建向量 vec,用于存放 std::shared_ptr<A>。
使用不同的方法向 vec 中添加元素:
使用 std::shared_ptr<A>(new A(1), func) 创建一个智能指针,并使用自定义删除器。
使用 std::make_shared<A>(2) 创建一个智能指针。
使用 vec.emplace(vec.end(), new A(3)) 在向量的末尾直接构造一个对象。
使用 vec.emplace_back(new A(4)) 在向量的末尾直接构造一个对象。
使用 vec.insert(vec.end(), { std::make_shared<A>(5),std::shared_ptr<A>(new A(6), func) })
        在向量的末尾插入两个元素。
通过循环删除向量的所有元素,以展示自定义删除器的使用。

第二个代码块:在这个代码块中,我们使用 std::shared_ptr 来管理一个包含 A 对象的 std::vector。
定义另一个 lambda 函数 func,它接受一个指向 std::vector<A> 的指针,并清空向量并删除它。
创建 std::shared_ptr<std::vector<A>> ptr,并使用 lambda 函数作为删除器。
然后重置 ptr 以触发删除器。
*/


c++ 疑难杂症(1) std::thread

c++ 疑难杂症(2) std::move

c++ 疑难杂症(3) 模板特化

c++ 疑难杂症(5) std::pair

c++ 疑难杂症(6) std::map

c++ 疑难杂症(7) std::tuple

c++ 疑难杂症(8) std::multimap

最近发表
标签列表