我有一个字符串向量作为我的主要数据容器。但是,为了与C库互操作,我需要能够将这些字符串视为字符数据指针(即const char*
)。这听起来很简单,所以我写了一个这样的辅助类:
class strvecAccessor {
const std::vector<std::string>& names;
public:
strvecAccessor(const std::vector<std::string>& a) : names(a) {}
size_t size() const {
return names.size();
}
const char* item(size_t i) {
auto name = names[i];
return name.data();
}
};
此访问器类是短暂的。它用作现有字符串向量的包装,保证在此类的生存期内不会被修改或超出范围。下面是如何使用此类的一个示例:
void test(strvecAccessor& arr) {
for (size_t i = 0; i < arr.size(); ++i) {
printf("%s\n", arr.item(i));
}
}
但是此代码中存在一个错误,该错误仅在我以--coverage -O0
模式进行编译时才会出现,并且仅在Unix计算机上(我在C ++ 11兼容模式下使用CLang 6.0.0进行编译)才会显现出来。错误在于打印的字符串包含垃圾。
我相信会发生的事情是,name
方法中的item()
变量不是引用,而是数组的第i
个元素的副本。它在item()
函数的末尾超出范围,此时返回的指针会悬空。在大多数情况下,由于指针会立即使用,因此并不明显,但是在覆盖模式下,调用后立即将其填充其他数据。
如果我将auto name = names[i];
替换为const std::string& name = names[i];
,问题将消失。但是我真的不明白为什么,这是否真正解决了这个问题,或者只是将其深埋。所以我的问题是:为什么要用原始代码复制?以及将来如何保护自己免受此类错误的侵害?
答案 0 :(得分:1)
const char* item(size_t i) {
auto name = names[i];
return name.data();
}
在这里,name
是函数item()
的局部变量,您将向该局部变量拥有的数据返回地址。当它超出范围(item()
函数完成)时,name
将被销毁。
由于要保证基础向量的生命周期,请尝试以下方法:
const char* item(size_t i) {
return names[i].data();
}
这将是“安全的”,因为vector::operator[]
返回对存储数据的引用,并且您不会像原始变量那样将多余的副本复制到name
变量中。