分配一个指向临时对象的指针——如何防止它?
Posted
技术标签:
【中文标题】分配一个指向临时对象的指针——如何防止它?【英文标题】:Assigning a pointer to a temporary object -- how to prevent it? 【发布时间】:2013-12-27 00:51:42 【问题描述】:我的班级有一个具有以下原型的方法:
std::string
Block::get_field(std::string rec_type, std::string field) ...
它从映射中检索一个值,将其转换为字符串,然后返回该字符串。
有人使用我的类如下:
char * buf = block.get_field(my_rec, my_field).c_str();
除非我弄错了,否则这是将 buf 设置为指向临时字符串。不久之后,当他查看 *buf 时,它已损坏。
显然我更希望我的用户写:
std::string buf = block.get_field(my_rec, my_field);
作为 Block 的作者,我如何防止此类滥用行为?或者有什么方法可以支持这种用法?
更新 1: 直到我更改了映射的实现,从直接保存值到保存“内存引用”——一个长度和一个指向缓冲区的指针,这个 bug 才被发现。但是get_field
总是返回一个字符串——不是string*
或string&
——所以我认为它总是返回一个临时的。我不明白为什么它之前没有中断(我也很尴尬;我声称我的更改不会影响 API)。
我准备通知用户他必须修改他的代码,但我希望能够引用他违反的“规则”,并可能解释他之前是如何“走运”的.
更新 2: 似乎有可能(参考更新 1),刚才出现错误的原因是我的“幕后”更改要求我添加以下开关g++ 4.4.7:-std=gnu++0x,这可能影响了临时对象的回收方式。
【问题讨论】:
记录下来,然后上路。std::stringstream
也有同样的问题。
这是一个直接的错误(在您的用户方面)。
嗯,返回隐式转换为 std::string 并在类型上禁止 .c_str() 的内容?
无需记录。我认为你的用户会知道 C++ 是公平的。否则,您需要在每个函数之前的注释中添加完整的语言教程。
@KerrekSB,我可以选择任何一种方式。我猜这和字符串流之一会很高兴提醒。不是要专门学习的东西,而是有助于减少错误的提醒。
【参考方案1】:
大多数情况下,您必须假设您的代码的用户是有能力的。
但这是一个政治世界。因此,有时您可能必须尽最大努力防止事故发生。例如,
#include <assert.h>
#include <string>
class StdString: public std::string
private:
using std::string::c_str;
using std::string::data;
public:
StdString( char const* const s ): std::string( s ) assert( s != nullptr );
;
StdString foo() return "Bah!";
int main()
std::string const a = foo();
char const* const b = foo().c_str(); //! Nyet.
也许为了更好的衡量标准,在那里加入一个移动构造函数,但通常完美的效率与完美的安全性和完美的清晰度相冲突,例如可以有 2 但没有 3。
【讨论】:
另一方面,char const* const b = a.c_str();
是安全的,不是吗?
这个断言永远不会触发,不是吗? string
构造函数在获得空指针时已经抛出,或者至少此时它是 UB。
@KerrekSB:“它是 UB”是正确的可能性,“将已经抛出”是错误的可能性:不能保证抛出或失败,这就是 assert
的原因。
【参考方案2】:
你真的不应该阻止它,但如果你发现这种情况经常发生,你可以将原型更改为:
void
Block::get_field(std::string& ret, std::string rec_type, std::string field) ...
引用:std::string& ret
将在调用您的方法之前强制 std::string 存在,而他们仍然可以做一些事情来破坏他们的记忆,这将发生在“他们的代码”中。像这种糟糕的代码这样简单的事情很难预防:
char * foo()
std::string buffStr;
block.get_field(bufStr, my_rec, my_field)
return buffStr.c_str(); // DO NOT DO THIS - Example of bad code
但至少有一个内存检查器会找到他们的分配,而不是你的
更新 1:
c_str()
返回的内存的生命周期是生成它的 string
的生命周期,除非 string
被更改,在这种情况下生命周期可能会结束。基本上,这是未定义的行为,有时可能会起作用,而其他情况则不起作用。您的更改可能已经改变了观察到的行为,但用户代码在任何时候都不符合。其他因素也可能破坏它。基本上,它总是坏掉,从你的描述看来没有理由道歉。
【讨论】:
所以,听起来他的错误的症结在于使用 c_str() 而不了解它的陷阱。 Stroustrup 谈到了保持对象的“不变性”的重要性,而 c_str() 允许您直接跳过接口并进入实现。那么,我猜 c_str() 应该不被接受。 @Chap 它本身不是 c_str(),虽然它发现了许多陷阱,但问题是string
“拥有”内存,所以你必须保留它。只要你能保证string
在附近,使用 c_str() 基本上是安全的。您发现的问题是,情况并非总是如此。 (人们也知道从 c_str() 返回时调用 strtok)以上是关于分配一个指向临时对象的指针——如何防止它?的主要内容,如果未能解决你的问题,请参考以下文章