作者poyenc (发箍)
看板C_and_CPP
标题Re: [问题] 是否使用vector?
时间Sat Feb 2 01:39:08 2019
※ 引述《clonsey1314 (Clonsey)》之铭言:
: 最近刚接触vector, 很方便, 省了很多初始化的工作
: 程式码也变得简洁多, 也较好维护
: 但是同时也降低的程式的效能(很明显)
: 请问若没有要做太多复杂的增删,是否继续使用array或pointer就好?
: 程式码里同时有vector和pointer/array混杂这样的coding style会不会不好?
: 谢谢
一般在 std::vector 和 C array 间作选择的考量点主要在
元素个数的
决定时机: 如果元素个数在
编译时期可以决定, 当然使用 C array 或
std::array; 如果只能在
执行时期决定, 才需要考虑 std::vector, 但
也不是预设就使用 STL 容器, 端看你的使用情境.
简单举个例子: 假如我们现在需要一块连续记忆体来储存班上同学的期
末考成绩, 班上同学的数目已知是 50 位, 分数是非负整数, 就可以用
std::array 来定义:
using score_type =
unsigned;
std::array<score_type,
50> scores;
然後可能因为效能需求, 或到某个时机点才需要将这块记忆体配置出来
(lazy initialization), 可以考虑 std::unique_ptr + std::array:
std::unique_ptr<std::array<score_type,
50>> scores;
需要注意的是, 这里和 std::unique_ptr<score_type[]> 的差别在於
元素个数的决定时机, 如果想使用後者, 代码的长相需要像这样:
std::size_t n;
std::cin >> n;
auto scores = std::make_unique<score_type[]>(n);
两者表达的
语意完全不一样. 另外 std::vector 虽然简化了物件初始
化及复制, 但其附带的
可变长度性质, 也不适合用来描述上面的问题.
所以
用什麽型别不是问题, 问题是有没有用对型别. 最後整理给你作参
考:
(考虑 owning 语意)
元素个数在
编译时期决定:
不需要改变大小: std::array<T, N>
元素个数在
执行时期决定:
需要改变大小: STL container
不需要改变大小: std::unique_ptr<T[]>
选用何种 STL 容器就看需要哪些操作 (存取方式、存取成本、新增/删
除元素的位置等..) 假如会在後端以外的地方新增元素, std::vector
就不是好的选择.
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 123.193.76.85
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/C_and_CPP/M.1549042751.A.DB1.html
1F:推 ilikekotomi: 感谢整理与分享 02/02 13:40
2F:→ tomsawyer: 题外话 请问如何存取vector string里面的字元? 02/02 18:02
3F:推 achicn3: s1[0][0]这样吧?楼上 02/02 18:22
4F:推 tomsawyer: 我试试看 谢谢楼上 02/02 18:57
比较好的方式是:
using std::next;
const std::vector<std::string> vs{
"hello"s,
"world"s};
const auto idx =
1z;
// or const std::ptrdiff_t idx = 1; in pre-c++20
[[assert: idx < size(vs)]];
const std::string_view sv = *next(begin(vs), idx);
[[assert: sv[1] == 'o']];
虽然 std::vector 就有提供 operator[] 用来存取元素, 但并不是所有
的 STL 容器都有提供相同的介面, 当你写下 vs[..] 的时候, 增加了对
容器的需求(requirement), 导致抽换容器实作的时候为了满足(satisfy)
原本的需求必须实作这些介面.
不管是 generic programming 或是想要提供复用性更高的程式码,
对容
器的操作尽可能地局限在迭代器(iterator) 上是比较好的, 使用比较通
用的介面未来扩充性也比较高. 譬如上面这个例子, 若把 next()呼叫改
为:
begin(vs) + idx
// bad example
这个操作需要迭代器满足
RandomAccessIterator 概念(concept), 因为
std::vector 本身满足 ContiguousContainer, 很容易就会使用到不必要
的操作,
是比较不好的用法. 使用 begin() 让 ADL(argument dependent
lookup) 寻找合适的呼叫 (包含自定义函式), 多了 next() 间接呼叫让
ForwardIterator 也能当作引数, 这段码的复用性就大大地提高了.
最後建议透过 std::span 或 std::string_view 甚至是 ranges library
里的适配器(adaptor)来存取容器元素, 也可以
减少对容器介面的依赖.
5F:推 allensheng: 最近的文都太专业了吧 02/03 01:43
※ 编辑: poyenc (123.193.76.85), 02/03/2019 17:30:32
6F:推 leoloveivy: 刚转回C/CPP版真D没让我失望XDDD 推个 02/03 19:42