教训一、map[key] 会向map插入一条空记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| unordered_map<string, string> map; map.emplace("key1", "value1"); map.emplace("key2", "value2");
cout<<"value="<<map["key3"]<<endl;
auto iter = map.find("key3"); if (iter != map.end()){ cout<<"key3 found!"<<endl; }else{ cout<<"key3 not found!"<<endl; }
cout<<"value="<<map["key3"]<<endl;
|
运行结果:
1 2 3
| value= key3 found! value=
|
key3居然能在map中通过find函数查到。
正确的用法是:
C++里,map[key] 只能用于存值,不能用于取值。
文档如下:如果key不存在,则插入一条记录,值为默认值。

举一个经典的场景, 统计一个字符串数组里每个字符串出现的次数,正确做法是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| unordered_map<string, int> map; string array[] = {"a", "a", "b", "c", "c", "c"}; for (string item : array) { if (map.find(item) != map.end()) { map[item]++; } else { map[item] = 1; } } cout << "{"; for (auto it = map.begin(); it != map.end(); it++) { cout << "\"" << it->first << "\": " << it->second; if (next(it) != map.end()) { cout << ", "; } } cout << "}" << endl;
|
运行结果:
1
| {"c": 3, "a": 2, "b": 1}
|
教训二、 C++里的 getter 方法可能会发生值拷贝:
猜猜下面的代码会输出什么?:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| class Test{ public: Test() { cout<<"list real address = "<<&list<<endl; }
private: vector<string> list;
public: void addToList(const string & item){ list.emplace_back(item); } vector<string> getList(){ return list; } vector<string> & getListPointer(){ return list; } }; inline string listToString(const vector<string> & list){ string result = "{"; for(const auto & str : list){ result += str + ", "; } if (!list.empty()) { result.pop_back(); result.pop_back(); } result = result + "}"; return result; } inline void test1() { Test t; t.addToList("1"); const auto list1 = t.getList(); const auto & list2 = t.getList();
const auto list3 = t.getListPointer(); const auto & list4 = t.getListPointer();
cout << "list1 address: " << (&list1) <<", value=" <<listToString(list1) << endl; cout << "list2 address: " << (&list2) <<", value=" <<listToString(list2) << endl; cout << "list3 address: " << (&list3) <<", value=" <<listToString(list3) << endl; cout << "list4 address: " << (&list4) <<", value=" <<listToString(list4) << endl;
}
|
输出:
1 2 3 4 5
| list real address = 0x7ffeee78a8e0 list1 address: 0x7ffeee78a900, value={1} list2 address: 0x7ffeee78a920, value={1} list3 address: 0x7ffeee78a940, value={1} list4 address: 0x7ffeee78a8e0, value={1}
|
也就是说,只有list4才是原地址,另外3种都发生了值的拷贝,都创建了新的list。如果list里的内容较多,可能就会严重影响性能。
教训三、bool 类型的默认值可能不是 false,int 的默认值可能不是0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Test{ public: Test() {}
public: bool a{}; int b; bool c; };
inline void test1() { Test t; cout << "a: " << (t.a ? "true" : "false") << endl; cout << "b: " << (t.b ) << endl; cout << "c: " << (t.c ? "true" : "false") << endl; } int main() { test1(); return 0; }
|
执行结果:
1 2 3
| a: false b: 1907956496 c: true
|
a和c同为bool,初始化方式不一样,导致其值也不一样。b的类型是int,其默认值并不是0。
建议:做好初始化!
教训四、不要忽略 IDE 的警告:
1 2 3 4 5 6
| string foo(){ return "xxxx"; }
const char *c1 = foo().c_str(); //这种用法看上去没问题吧
|
但IDE给出了警告:
.c_str().png)
参考:C++备忘录014:函数返回临时变量的生命周期
教训五、函数没写返回值,IDE也不会报错(起码Clion没有报错)
1 2 3 4 5 6 7
| int test(int a){ cout<<"a="<<a<<endl; }
int main() { test(1); }
|
这样的代码在 Ubuntu 上的确可以执行成功, 但是在别的 Linux 平台, 可能会出现难以排查的内存错误!
建议经常使用IDE的代码检查工具“Code -> Inspect Code”检查下代码:

待续。