當關乎效率時應該在map::operator[]和map-insert之間仔細選擇
class Widget {
public:
Widget();
Widget(double weight);
Widget& operator=(double weight);
...
}
map<int, Widget> m;
m[1] = 1.50;
運算式m[1]是m.operator[](1)的簡化,所以這是一個map::operator[]的調用。那個函數必須返回一個Widget的
引用,因為m 的映射類型是Widget。在這裡,m裡面還沒有任何東西,所以鍵1在map裡沒有入口。因此
operator[]預設構造一個Widget來作為關聯到1的值,然後返回到那個Widget的引用。最後,Widget成為賦值目
標:被賦值的值是1.50。
換句話說,這個語句
m[1] = 1.50;
功能上等價於這個:
typedef map<int, Widget> IntWidgetMap; // 方便的
// typedef
pair<IntWidgetMap::iterator, bool> result = // 用鍵1建立新
m.insert(IntWidgetMap::value_type(1, Widget())); // 映射入口
// 和一個預設構造的
// 值對象;
// 看下面對於
// value_type的
// 注釋
result.first->second = 1.50; // 賦值給
// 新構造的
條款24:當關乎效率時應該在map::operator[]和map-insert之間仔細選擇
// 實值型別
現在已經很清楚為什麼這種方法可能降低效能了。我們先預設構造一個Widget,然後我們立即賦給它新值。
如果用想要的值構造Widget比預設構造Widget然後進行賦值顯然更高效,我們就應該用直截了當的insert調用
來替換operator[]的使用(包括它的構造加賦值):
m.insert(IntWidgetMap::value_type(1, 1.50));
這與上面的那些代碼有相同的最終效果,除了它通常節省了三次函數調用:一個建立臨時的預設構造Widget
對象,一個銷毀那個臨時的對象和一個對Widget的賦值操作。那些函數調用越昂貴,你通過使用map-insert代
替map::operator[]就能節省越多。
上面的代碼利用了每個標準容器都提供的value_type typedef。這typedef沒有什麼特別重要的,但對於map和
multimap(以及非標準容器的hash_map和hash_multimap——參見條款25),記住它是很重要的,容器元素的
類型總是某種pair。
我早先談及的operator[]被設計為簡化“添加或更新”功能,而且現在我們理解了當“增加”被執行時,insert
比operator[]更高效。當我們做更新時,情形正好相反,也就是,當一個等價的鍵(參見條款19)這已經在
map裡時。為了看出為什麼情況是這樣,看看我們的更新選項:
m[k] = v; // 使用operator[]
// 來把k的值
// 更新為v
m.insert(
IntWidgetMap::value_type(k, v)).first->second = v; // 使用insert
// 來把k的值
// 更新為v
文法本身也許會讓你信服地支援operator[],但在這裡我們關注於效率,所以我們將忽略它。insert的調用需要
IntWidgetMap::value_type類型的實參(即pair<int, Widget>),所以當我們調用insert時,我們必須構造和析構
一個那種類型的對象。那耗費了一對建構函式和解構函式,也會造成一個Widget的構造和析構,因為
pair<int, Widget>本身包含了一個Widget對象,operator[]沒有使用pair對象,所以沒有構造和析構pair和
Widget。
因此出於對效率的考慮,當給map添加一個元素時,我們斷定insert比operator[]好;而從效率和美學考慮,當
更新已經在map裡的元素值時operator[]更好。如果STL提供一個兩全其美的函數,即,在句法上迷人的包
中的高效的“添加或更新”功能。