Day12-2 访问容器中的元素
下面种遍历方式都能正确访问 std::vector<std::unique_ptr<int>> 中的元素,但它们各有特点和潜在问题点,我们逐一分析:
✅ 第一种:范围 for + 自动类型推导
for (auto& value : vec)
{cout << *value << endl;
}
✔ 优点:
- 简洁且安全。
value是std::unique_ptr<int>&,不会发生值拷贝或所有权转移。- 推荐做只读或修改指针指向的内容时使用。
⚠ 缺点:
- 仅当你熟悉
auto推导规则时清晰易懂;否则不显式写类型可能阅读性略差。
✅ 第二种:范围 for + 显式类型
for (std::unique_ptr<int>& value : vec)
{cout << *value << endl;
}
✔ 优点:
- 显式指出变量类型,阅读性更强,对学习者更友好。
- 同样是引用,不会转移所有权。
⚠ 缺点:
- 相较于
auto&略显啰嗦,但功能等价。
✅ 第三种:使用下标访问
for (size_t i = 0; i < vec.size() ; i++)
{cout << *vec[i];cout << vec.at(i);
}
✔ 优点:
- 可以使用下标
i,方便访问前后元素(如 i+1,i-1)。 - 支持
vec.at(i),带越界检查。
⚠ 缺点:
- 每次访问都需调用
operator[]或at(),可能比迭代器略慢(虽然差别极小)。 vec.at(i)会抛异常,vec[i]不会,容易写出越界访问错误。- 不如基于范围的 for 简洁。
总结建议:
| 遍历方式 | 安全性 | 可读性 | 性能 | 推荐用途 |
|---|---|---|---|---|
for (auto& value : vec) | 高 | 中-高 | 高 | 推荐日常遍历 |
for (std::unique_ptr<int>& value : vec) | 高 | 高 | 高 | 学习阶段推荐 |
for (size_t i = 0; i < vec.size(); ++i) | 中 | 中 | 中 | 需访问索引或相邻元素时使用 |
如果你不打算修改指针本身,也可以加上 const 修饰,提升表达意图的清晰度:
for (const std::unique_ptr<int>& value : vec)
{cout << *value << endl;
}
或者:
for (const auto& value : vec)
{cout << *value << endl;
}
你已经用了三种遍历方式来访问 std::vector<std::unique_ptr<int>> 中的元素:
- 基于范围的 for 循环(引用)
- 显式使用引用类型的基于范围的 for 循环
- 使用索引
vec[i]和vec.at(i)
除了这些,还有以下几种方式可以遍历 vec:
✅ 第四种:使用迭代器(推荐理解底层原理)
for (auto it = vec.begin(); it != vec.end(); ++it) {cout << *(*it) << endl; // *it 是 unique_ptr,*(*it) 是 int
}
✅ 第五种:使用 std::for_each 算法
std::for_each(vec.begin(), vec.end(), [](const std::unique_ptr<int>& ptr) {cout << *ptr << endl;
});
✅ 第六种:使用 std::ranges::for_each(C++20)
#include <ranges>std::ranges::for_each(vec, [](const std::unique_ptr<int>& ptr) {cout << *ptr << endl;
});
✅ 第七种:使用 transform 做一些额外操作(如果你需要构造另一个容器)
std::vector<int> values;
std::transform(vec.begin(), vec.end(), std::back_inserter(values),[](const std::unique_ptr<int>& ptr) {return *ptr;});for (int val : values) {cout << val << endl;
}
小提示
如果你只是访问 unique_ptr 中的值,用 const std::unique_ptr<int>& 是最好的方式,因为:
- 不会移动所有权
- 不会复制指针
- 安全且高效
好问题!你问的是:
为什么迭代器遍历
vector<unique_ptr<int>>时需要两次解引用?
来看一下这段代码:
for (auto it = vec.begin(); it != vec.end(); ++it) {cout << *(*it) << endl; // 两次解引用
}
🌟 理解第一次解引用 \*it
it是一个 迭代器,类型为:std::vector<std::unique_ptr<int>>::iterator。- 所以
*it得到的是unique_ptr<int>&,即指向容器中某个unique_ptr<int>的引用。
🧠 理解第二次解引用 \*(\*it)
*it是unique_ptr<int>&,所以*(*it)就是访问unique_ptr所管理的对象,也就是int。- 所以最终你得到的是
int的值。

🧩 类比一下(更容易理解):
假设你有一个 vector<string*>:
std::vector<std::string*> vec;
vec.push_back(new std::string("Hello"));for (auto it = vec.begin(); it != vec.end(); ++it) {std::cout << **it << std::endl; // 同样是两次解引用
}
为什么也是 **it?因为:
*it是string*(一个裸指针)**it是string的内容
✅ 回到 unique_ptr<int> 的情况:
std::vector<std::unique_ptr<int>> vec;
vec[i]是一个unique_ptr<int>。- 所以
*vec[i]是int。 - 迭代器
it→*it是unique_ptr<int>&,再*(*it)就是int。
📝 总结口诀:
容器里装指针(包括智能指针),访问内容就要两次
\*。第一次
*拿到指针,第二次*拿到内容。
如果你想更简单一些,不用管两次 *,可以用:
for (const auto& ptr : vec) {std::cout << *ptr << std::endl;
}
它内部其实也是做了两次 *,只是你没写出来而已 😄

