Re: [問題] 是否使用vector?
※ 引述《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://www.ptt.cc/bbs/C_and_CPP/M.1549042751.A.DB1.html
推
02/02 13:40,
6年前
, 1F
02/02 13:40, 1F
→
02/02 18:02,
6年前
, 2F
02/02 18:02, 2F
推
02/02 18:22,
6年前
, 3F
02/02 18:22, 3F
推
02/02 18:57,
6年前
, 4F
02/02 18:57, 4F
比較好的方式是:
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)來存取容器元素, 也可以減少對容器介面的依賴.
推
02/03 01:43,
6年前
, 5F
02/03 01:43, 5F
※ 編輯: poyenc (123.193.76.85), 02/03/2019 17:30:32
推
02/03 19:42,
6年前
, 6F
02/03 19:42, 6F
討論串 (同標題文章)
C_and_CPP 近期熱門文章
PTT數位生活區 即時熱門文章