大纲
- 案例
- 代码地址
C++是一种强类型语言。我们在编码时就需要明确指出每个变量的类型,进而让编译器可以正确的编译。看似C++编译器比其他弱类型语言的编译器要死板,实则它也做了很多“隐藏”的操作。它会在尝试针对一些非预期类型进行相应转换,以符合预期,比如《C++拾趣——类构造函数的隐式转换》中提到的隐式转换。
但是也正因为这些“隐藏”的转换行为,让一些行为超出我们的预期。比如本文提及的案例,就是因为我们声明了一个不准确的类型变量,导致编译器为了让我们代码“合法”,进而做了一些隐式转换,导致程序性能下降。
我们也将通过这个案例,了解C++11引入auto关键字的必要性。
案例
先看下面这段可以运行的代码。
std::cout << "Create unordered_map" << std::endl;std::unordered_map<Custom, int> unordered_map;unordered_map[std::move(Custom(1))] = 1;unordered_map[std::move(Custom(2))] = 2;unordered_map[std::move(Custom(3))] = 3;std::cout << std::endl << "Traverse unordered_map using std::pair<Custom, int>" << std::endl;for (const std::pair<Custom, int>& pair : unordered_map) {std::cout << pair.first << " " << pair.second << std::endl;}
请问下面的代码有什么性能问题?
可能第一眼看过去,并不能发现它的问题所在。我们将这段代码的运行过程打印出来
可以发现在遍历的过程中,发生了Custom对象的复制和析构。
如果Custom对象比较大,就会引发性能问题。
这是因为std::unordered_map的Key是const类型,即我们应该如下方式遍历
for (const std::pair<const Custom, int>& pair : unordered_map) {std::cout << pair.first << " " << pair.second << std::endl;}
编译器在发现我们没有使用const Custom时,会自己推理并转换以符合我们书写的代码。这样就会导致一次const Custom向Custom复制的一次构造。
正因为这些犄角旮旯的知识导致编写健壮高效的C++代码比较困难。但是在C++11中引入的auto就可以很大的缓解我们的心智负担。我们可以这么写上述代码
for (const auto& pair : unordered_map) {std::cout << pair.first << " " << pair.second << std::endl;}
这样编译器会帮我们推导出正确的类型。
代码地址
https://github.com/f304646673/cpulsplus/tree/master/traverse_unordered_map