上一课标准答案*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。在这种数组访问情况下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::vectorint data;拆开std::vector ↓ 我要创建一个 vector int ↓ 里面存 int data ↓ 名字叫 data所以std::vectorint data;可以理解成创建一个叫data的动态整数数组。4. 为什么有int这是一个新写法。std::vectorint意思是这个vector专门存int。例如std::vectordouble angles;表示angles里面保存double。机械臂角度就很适合std::vectordouble joint_angles;5. 创建时直接放数据例如std::vectorint data {10, 20, 30};可以想成data [0] [1] [2] ┌────┬────┬────┐ │ 10 │ 20 │ 30 │ └────┴────┴────┘和数组很像。访问方式也很像data[0] data[1] data[2]所以std::cout data[1] std::endl;输出206.vector最重要的能力push_back比如std::vectorint 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::vectorint 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 309. 怎么知道vector里有多少元素使用data.size()例如std::vectorint data {10, 20, 30};那么data.size()得到3所以可以std::cout data.size() std::endl;输出310.size()为什么有括号因为size()本质上也是一个函数。你之前学过calculateError()现在data.size()可以先理解成调用属于data的size()函数询问它现在有多少个元素。11.vector for非常常用例如std::vectordouble 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::vectordouble 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::vectordouble angle {10, 20, 30};执行angle[1] 100;以后10 100 3015. 一个完整机器人例子#include iostream #include vector int main() { std::vectordouble 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: 6016.vector仍然从 0 开始和数组一样std::vectorint data {10, 20, 30};对应data[0] 10 data[1] 20 data[2] 30所以第 1 个元素还是[0]。17.vector也可能越界例如std::vectorint data {10, 20, 30};只有[0] [1] [2]如果写data[100]仍然是错误访问。所以用了vector不代表再也不用管下标。18.at()一种更安全的访问方式vector还可以data.at(1)例如std::vectorint 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];特点长度固定结构简单底层常见。vectorstd::vectordouble q;特点长度可以动态变化而且带很多方便的功能。例如q.push_back(...) q.size() q.at(...)20. 机器人里面什么时候用数组像六轴机械臂永远就是 6 个关节这时候固定大小很自然。以后现代 C 里甚至可能用std::arraydouble, 6这个我们以后再讲。21. 什么时候用vector例如轨迹点数量不固定 视觉检测目标数量不固定 点云数量很多 传感器采样数量变化 规划路径长度变化这时候std::vector通常非常合适。22. 一个非常接近以后机器人的场景假设路径规划生成若干关节轨迹点std::vectordouble 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::vectordouble表示一组数字。再以后甚至可能std::vectorstd::vectordouble表示很多组数字。例如轨迹点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::vectorint data;添加data.push_back(10);获取数量data.size();访问data[0];先把这四个吃透。本课小练习第 1 题std::vectorint data {10, 20, 30};那么data[0] ? data[2] ? data.size() ?第 2 题std::vectorint data; data.push_back(5); data.push_back(10); data.push_back(15);现在data里面依次有什么第 3 题std::vectordouble 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本质上都是字符串。
阅读完成 · 觉得有帮助?