拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

C++ 第 19 课:std::vector

C++ 第 19 课:std::vector

上一课标准答案:

    *p = 10

    因为p指向a[0]。

      *(p + 2) = 30

      因为:

      p → a[0] p + 1 → a[1] p + 2 → a[2]

      所以:

      *(p + 2)

      就是访问a[2]。

        *p = 10

        因为:

        int a[3] = {5, 10, 15}; int* p = a;

        开始时:

        p → a[0] → 5

        执行:

        p++;

        以后:

        p → a[1] → 10

        因此:

        *p

        得到10。

        1. 在这种数组访问情况下:
        a[2]

        和:

        *(a + 2)

        访问的是同一个元素,也就是数组第 3 个元素。

        你现在可以先记:

        a[i] ↔ *(a + i)

        第 19 课:std::vector

        这一课开始接触现代 C++ 中非常常用的容器。

        前面我们学了数组:

        double joint[6];

        数组很好用,但是它有一个明显特点:

        长度通常在创建时就确定了。

        例如:

        double joint[6];

        就是 6 个。

        但现实程序经常遇到:

        我现在还不知道最终会有多少数据。

        例如机械臂轨迹:

        第1个轨迹点 第2个轨迹点 第3个轨迹点 ……

        可能今天是 20 个点,明天是 500 个点。

        这种情况下:

        std::vector

        非常好用。


        1.vector最直觉的理解

        你可以先把:

        std::vector

        理解成:

        长度可以变化的数组。

        普通数组:

        int a[3];

        像固定长度的一排盒子:

        ┌────┬────┬────┐ │ │ │ │ └────┴────┴────┘ 固定 3 个

        而vector更像:

        开始: ┌────┬────┐ │ │ │ └────┴────┘ 后来: ┌────┬────┬────┬────┐ │ │ │ │ │ └────┴────┴────┴────┘

        它可以动态增加元素。


        2. 使用vector要先准备什么?

        和:

        std::cout

        需要:

        #include <iostream>

        类似,

        使用vector通常需要:

        #include <vector>

        所以程序可能写:

        #include <iostream> #include <vector>

        其中:

        <vector>

        给我们提供std::vector。


        3. 创建第一个vector

        例如:

        std::vector<int> data;

        拆开:

        std::vector ↓ 我要创建一个 vector <int> ↓ 里面存 int data ↓ 名字叫 data

        所以:

        std::vector<int> data;

        可以理解成:

        创建一个叫data的动态整数数组。


        4. 为什么有<int>?

        这是一个新写法。

        std::vector<int>

        意思是:

        这个vector专门存int。

        例如:

        std::vector<double> angles;

        表示:

        angles里面保存double。

        机械臂角度就很适合:

        std::vector<double> joint_angles;

        5. 创建时直接放数据

        例如:

        std::vector<int> data = {10, 20, 30};

        可以想成:

        data [0] [1] [2] ┌────┬────┬────┐ │ 10 │ 20 │ 30 │ └────┴────┴────┘

        和数组很像。

        访问方式也很像:

        data[0] data[1] data[2]

        所以:

        std::cout << data[1] << std::endl;

        输出:

        20

        6.vector最重要的能力:push_back

        比如:

        std::vector<int> data;

        刚创建时里面没有元素:

        data 空

        然后:

        data.push_back(10);

        意思是:

        在 vector 最后增加一个10。

        现在:

        [10]

        再:

        data.push_back(20);

        变成:

        [10, 20]

        再:

        data.push_back(30);

        变成:

        [10, 20, 30]

        7.push_back怎么读?

        data.push_back(10);

        这里出现:

        .

        我们以后学类的时候会正式解释。

        现在先理解为:

        对data这个 vector 执行一个操作。

        所以:

        data.push_back(10);

        大致读作:

        data,往最后塞进去一个10。


        8. 完整例子

        #include <iostream> #include <vector> int main() { std::vector<int> data; data.push_back(10); data.push_back(20); data.push_back(30); std::cout << data[0] << std::endl; std::cout << data[1] << std::endl; std::cout << data[2] << std::endl; return 0; }

        输出:

        10 20 30

        9. 怎么知道vector里有多少元素?

        使用:

        data.size()

        例如:

        std::vector<int> data = {10, 20, 30};

        那么:

        data.size()

        得到:

        3

        所以可以:

        std::cout << data.size() << std::endl;

        输出:

        3

        10.size()为什么有括号?

        因为:

        size()

        本质上也是一个函数。

        你之前学过:

        calculateError()

        现在:

        data.size()

        可以先理解成:

        调用属于data的size()函数,询问它现在有多少个元素。


        11.vector + for非常常用

        例如:

        std::vector<double> joint_angles = { 10.0, 20.0, 30.0, 40.0, 50.0, 60.0 };

        我们可以:

        for (int i = 0; i < joint_angles.size(); i++) { std::cout << joint_angles[i] << std::endl; }

        逻辑就是:

        i = 0 ↓ 访问 joint_angles[0] i = 1 ↓ 访问 joint_angles[1] ……

        12. 为什么不用直接写i < 6?

        当然可以:

        for (int i = 0; i < 6; i++)

        但如果 vector 后来增加了一个元素:

        joint_angles.push_back(70.0);

        现在已经有 7 个元素。

        如果还写:

        i < 6

        就只能处理前 6 个。

        而:

        i < joint_angles.size()

        会自动根据当前元素数量变化。

        这就是更灵活的地方。


        13. 机械臂轨迹点例子

        假设你不断生成 Joint1 的轨迹角度:

        std::vector<double> trajectory;

        开始为空。

        然后:

        trajectory.push_back(10.0); trajectory.push_back(15.0); trajectory.push_back(20.0); trajectory.push_back(25.0);

        现在:

        trajectory [0] [1] [2] [3] 10.0 15.0 20.0 25.0

        也就是说:

        我们不用一开始就知道轨迹到底有几个点。

        这对机器人轨迹特别实用。


        14. 可以修改元素

        和数组一样:

        std::vector<double> angle = {10, 20, 30};

        执行:

        angle[1] = 100;

        以后:

        10 100 30

        15. 一个完整机器人例子

        #include <iostream> #include <vector> int main() { std::vector<double> joint_angles; joint_angles.push_back(10.0); joint_angles.push_back(20.0); joint_angles.push_back(30.0); joint_angles.push_back(40.0); joint_angles.push_back(50.0); joint_angles.push_back(60.0); for (int i = 0; i < joint_angles.size(); i++) { std::cout << "Joint " << i + 1 << ": " << joint_angles[i] << std::endl; } return 0; }

        输出:

        Joint 1: 10 Joint 2: 20 Joint 3: 30 Joint 4: 40 Joint 5: 50 Joint 6: 60

        16.vector仍然从 0 开始

        和数组一样:

        std::vector<int> data = {10, 20, 30};

        对应:

        data[0] = 10 data[1] = 20 data[2] = 30

        所以:

        第 1 个元素还是[0]。


        17.vector也可能越界

        例如:

        std::vector<int> data = {10, 20, 30};

        只有:

        [0] [1] [2]

        如果写:

        data[100]

        仍然是错误访问。

        所以用了vector不代表:

        再也不用管下标。


        18.at():一种更安全的访问方式

        vector还可以:

        data.at(1)

        例如:

        std::vector<int> data = {10, 20, 30}; std::cout << data.at(1) << std::endl;

        输出:

        20

        它和:

        data[1]

        都能访问第二个元素。

        但是:

        data.at(100)

        发生越界时,通常会明确抛出异常,而:

        data[100]

        通常不会主动帮你检查。

        现阶段你可以理解成:

        [] 更直接、更常见 at() 带边界检查,更安全一些

        19.vector和普通数组最大的区别

        普通数组:

        double q[6];

        特点:

        长度固定,结构简单,底层常见。

        vector:

        std::vector<double> q;

        特点:

        长度可以动态变化,而且带很多方便的功能。

        例如:

        q.push_back(...) q.size() q.at(...)

        20. 机器人里面什么时候用数组?

        像六轴机械臂:

        永远就是 6 个关节

        这时候固定大小很自然。

        以后现代 C++ 里甚至可能用:

        std::array<double, 6>

        这个我们以后再讲。


        21. 什么时候用vector?

        例如:

        轨迹点数量不固定 视觉检测目标数量不固定 点云数量很多 传感器采样数量变化 规划路径长度变化

        这时候:

        std::vector

        通常非常合适。


        22. 一个非常接近以后机器人的场景

        假设路径规划生成若干关节轨迹点:

        std::vector<double> trajectory;

        规划过程中不断:

        trajectory.push_back(10.0); trajectory.push_back(12.5); trajectory.push_back(16.0); trajectory.push_back(21.2);

        执行时:

        for (int i = 0; i < trajectory.size(); i++) { double target = trajectory[i]; std::cout << target << std::endl; }

        意思就是:

        按顺序取出每一个目标点。

        以后你真正做机械臂轨迹时,就是类似这种思想,只不过每个轨迹点可能不再只有一个double,而是六个关节角度。


        23. 后面会发展成这样

        未来你会看到:

        std::vector<double>

        表示一组数字。

        再以后甚至可能:

        std::vector<std::vector<double>>

        表示:

        很多组数字。

        例如:

        轨迹点1: [j1, j2, j3, j4, j5, j6] 轨迹点2: [j1, j2, j3, j4, j5, j6] 轨迹点3: [j1, j2, j3, j4, j5, j6]

        这就已经非常接近机械臂轨迹数据了。

        不过现在先不学二维 vector。


        24. 本课最重要的 4 个写法

        创建:

        std::vector<int> data;

        添加:

        data.push_back(10);

        获取数量:

        data.size();

        访问:

        data[0];

        先把这四个吃透。


        本课小练习

        第 1 题

        std::vector<int> data = {10, 20, 30};

        那么:

        data[0] = ? data[2] = ? data.size() = ?

        第 2 题

        std::vector<int> data; data.push_back(5); data.push_back(10); data.push_back(15);

        现在data里面依次有什么?

        第 3 题

        std::vector<double> q = {10, 20, 30}; q[1] = 50;

        最后q是什么?

        第 4 题

        为什么下面这种写法通常比固定写6更灵活?

        for (int i = 0; i < data.size(); i++)

        下一课我们继续 STL,但先学最常用、也最容易理解的:

        第 20 课:std::string

        你会搞清楚:

        'A'

        和:

        "A"

        到底有什么区别,以及为什么机器人程序里的:

        "CONNECTED" "ERROR" "/dev/ttyUSB0" "joint_state"

        本质上都是字符串。

        返回列表