让我们考虑以下不正确的代码:
#include <string>
#include <iostream>
struct C {
C(const std::string &_s): s(_s) { }
void run() {
std::cout << "Here we are using the string " << s << "\n";
}
const std::string &s;
};
int main() {
C a("/tmp/example.log");
a.run();
return 0;
}
g++ 6.3.0 和 clang 3.8.1 都会在没有警告的情况下编译此代码,即使使用-W -Wall -Werror
.
Here we are using the string Here we are usin
为什么结果不像人们天真地期望的那样?std::string
创建的对象的生命周期 const char *
仅限于构造函数的范围。因此,当foo()
构造函数返回时,该foo::s
字段具有对不存在对象的引用。卡布姆。
如果有人理解这个想法,很容易引入修复。它可以是显式创建std::string
对象并将其作为参数传递:
std::string s("/tmp/example.log");
C a(s);
另一种我认为更安全的方法是将foo::s
字段声明更改为:
const std::string s;
制作参数的本地不可修改副本,但它增加了复制对象的成本。好吧,在这个例子中,std::string 的复制不会自动导致进行深度复制,但对于自己实现的类来说,这可能是完全不同的事情。
对于 C++11,几乎没有其他可用的方法,如本答案中所指定,但是 - 例如 - 在嵌入式 Linux 环境中,当您坚持使用旧编译器和一些遗留代码时,它不一定是一个相当大的选择。
但是,无论程序员选择何种解决方案,都必须知道存在问题。即使我正确地编写了我的代码,我也愚蠢地认为编译器会足够聪明地检测出明显危险的情况,如果其他开发人员会陷入传递const char *
参数的陷阱。然而,我决定编写一些代码来检查编译器的行为,我很失望——很容易陷入引用已经不存在的对象的陷阱。
将临时对象作为 const 引用传递是一种公认的危险,它已在 C++11 中以某种方式解决,但仍将责任转嫁给程序员以积极抵制这种行为。那么,期望编译器发出警告是合理的,还是应该依赖外部静态分析工具?