Posted on May 28th, 2008 at 1:28 by fr3@K
見過不少 C/C++ Standard Library 的不愛用者. 要辨認他們並不困難, 從 code 中可以很容易發現:
- 在 header file 以
#pragma once替代標準 inclusion guard 手法 - 將一個
const long*宣告為CONST LPLONG - 習慣性使用
ZeroMemory/bzero與CopyMemory/bcopy, 而 (無意間?) 迴避memset與memcpy等標準 API
除了省去替 inclusion guard 取有效 (唯一) 名字的功夫之外,1 我實在是看不出上述習慣有任何實質上的好處.
把跟底層系統相關以及上層 UI 的 code 與中間 application 的 code 切開本身就是一件好處多多的事情, 通常也是一件該做的事. 只要模組化切割乾淨再加上使用標準 API, application 層多能不需要什麼修改就可以在多個平台 build and run.
這不但對 project 有 (或許不是立即的) 好處, 熟悉並使用標準的 programmer 常能夠容易地穿梭在不同平台與符合標準的 compiler 之間. 在我看來這對 programmer 也是一樣很有價值的資產.
我也接觸過不少人不喜歡標準容器. 寧願以 array 與其他 linked list 的 implementation 等等來代替標準容器. 他們說:
vector的迭代速度太慢- 不能把一個
list管理的 link node (不是 iterator) 轉移給另一個list - 標準容器不會自動清除如
SomeContainerTemplate<SomeType*>管理的 instance vectorallocate 的記憶體可能會大於實際所需. 縮小時也不會將多餘的記憶體釋放, 就連vector::clear()也一樣- 不熟悉標準容器的介面/行為, 如不清楚
SomeContainer到底在背後搞了什麼鬼. 或者是覺得標準容器設計的不好::resize(10)
根據我兩個多月前做的 benchmark, 在非 debug 的 configuration 下, vector 迭代的速度一點也不慢 (see benchmark results on GNU/Linux and Windows).
實際上是可以的. 見 list::splice.
我們得先搞清楚 SomeContainerTemplate<SomeType*> 所管理的 instance 是指向 SomeType 的 pointer, 而不是 SomeType 的 object. 若需要這樣的功能可以拿 boost::shared_ptr 之類的 smart pointer 搭配標準容器使用2, e.g.:
SomeContainerTemplate<shared_ptr<SomeType> >
縮小時不釋放多餘的記憶體其實是一種優化. 把一個有 m elements 的 vector 移除靠後面的 n 個 element 的時候, 不是呼叫 n 次 destructor 跟一次 deallocation 就好了. 是一次 allocation, m - n 次 uninitialized-copy (copy construction at uninitialized memory) 與呼叫 m 次 destructor 再加上一次 deallocation:
template <class T>
void shrink_back(
vector<T>& v,
size_t n /*number of elements to remove from back*/)
{
typename vector<T>::size_type m = v.size();
// Can not remove more than `m` elements.
n = min(n, m);
// Allocate memory then uninitialized-copy `m` - `n`
// elements.
// ps. Pretending the size of allocation is "just
// enough" to hold `m` - `n` elements, hence smaller
// then `v`.
vector<class T> tmp(v.begin(), v.begin() + (m - n));
// or simply:
// vector<class T> tmp(v.begin(), v.end() - n);
swap(v, tmp);
// `tmp` is destructed once gone out of scope:
// Calls dtor on `m` elements managed by `tmp`
// (previous managed by `v`), then deallocate
// memory.
}
沒錯, 躲不掉的 overhead, 即便是作在 vector 的實作裏面也是一樣. vector 的這個優化讓縮小時只需要做 n 次 uninitialize, 也因此擁有 no-throw 的 exception safety, 一舉兩得. 若真需要釋放多餘的記憶體, 可用下面的方法來取代 vector::clear():
template <class T>
void purge(vector<T>& v)
{
vector<T> tmp;
swap(v, tmp);
}
而 allocate 的記憶體可能會大於實際所需是另一個優化. 以減少當 vector 在長大時發生類似上面 copy and swap 的次數, 儘量跑在沒有直接 allocation 的 code path.
雖然我自己沒遇過, 這個優化的確在某些有嚴格記憶體限制的應用上可能造成使用者的困擾.
先不論熟悉標準庫本來就是靠 C++ 吃飯的 programmer 該有的專業技能. 更何況它不是一個你喜不喜歡的問題.
一般工作上寫的 code 不會是跟著 programmer, 而是屬於公司的. 若那天使用容器的 code 換別人負責, 或是原 programmer 離開服務單位了. 是接手的人通曉標準容器的可能性比較大? 還是剛好也熟悉於前一個 programmer 偏好的某種容器的機率比較高?
標準庫與標準容器都不是完美的, 各家的 implementation 也可能有略微的不同.3 更不是每個人都欣賞它背後的設計哲學. 但它畢竟是個標準, 不但有高度的品質保證, 使用說明與詳細文件更可說是隨手可得. 使用標準容器與 C++ Standard Library, 因為它是掛了保證的最大公因數.
#pragma once其實也有它潛在的麻煩 [↩]- Boost.PtrContainer 也有這樣的功能. 跟標準容器搭配 boost::shared_ptr 相比各有其優缺點 [↩]
- 我正在寫另一篇文字, 討論一個與標準容器相關的 (IMO) 重大瑕疵 [↩]
![]() |
|
| Previous Post « 不適任的 System Administrator « |
Next Post » CONST LPLONG » |








我還以為 #pragma once 是個標準的東西說
真是一個大收穫
之前就有發現 boost 都沒用 #pragma once
那時是懷疑是不是有些比較舊的Compiler 不支援
所以才沒用的
Comment by blair.ko — May 29, 2008 @ 11:10