经验一、map[key] 会向map插入一条空记载

  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; 

运转成果:

value=
key3 found!
value=

key3竟然能在map中经过find函数查到。

正确的用法是:

C++里,map[key] 只能用于存值,不能用于取值。

文档如下:假如key不存在,则插入一条记载,值为默认值。

记录 Java 转 C++ 开发时遇到的那些“坑”

举一个经典的场景, 统计一个字符数组里每个字符串呈现的次数,正确做法是:

    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;

运转成果:

{"c": 3, "a": 2, "b": 1}

经验二、 C++里的 getter 办法或许会产生值复制:

猜猜下面的代码会输出什么?:


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;
}

输出:

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

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;
}

履行成果:

a: false
b: 1907956496
c: true

a和c同为bool,初始化方式不一样,导致其值也不一样。b的类型是int,其默认值并不是0。

主张:做好初始化!

经验四、不要忽略 IDE 的正告:

string foo(){
    return "xxxx";
}
const char *c1 = foo().c_str();  //这种用法看上去没问题吧

但IDE给出了正告:

记录 Java 转 C++ 开发时遇到的那些“坑”

参考:C++备忘录014:函数回来临时变量的生命周期

经验五、函数没写回来值,IDE也不会报错(起码Clion没有报错)

int test(int a){
    cout<<"a="<<a<<endl;
}
int main() {
    test(1);
}

这样的代码在 Ubuntu 上确实可以履行成功, 但是在别的 Linux 平台, 或许会呈现难以排查的内存过错!

主张经常使用IDE的代码查看工具“Code -> Inspect Code”查看下代码:

记录 Java 转 C++ 开发时遇到的那些“坑”

待续。