19

可能重复:
临时工的生命周期

int LegacyFunction(const char *s) {
    // do something with s, like print it to standard output
    // this function does NOT retain any pointer to s after it returns.
    return strlen(s);
}

std::string ModernFunction() {
    // do something that returns a string
    return "Hello";
}

LegacyFunction(ModernFunction().c_str());

上面的例子可以很容易地重写为使用智能指针而不是字符串;这两种情况我都遇到过很多次了。无论如何,上面的示例将在 ModernFunction 中构造一个 STL 字符串,将其返回,然后在字符串对象中获取指向 C 样式字符串的指针,然后将该指针传递给遗留函数。

  1. ModernFunction 返回后存在一个临时字符串对象。什么时候超出范围?
  2. 编译器是否可以调用 c_str(),破坏这个临时字符串对象,然后将悬空指针传递给 LegacyFunction?(请记住,字符串对象正在管理 c_str() 返回值指向的内存...)
  3. 如果上面的代码不安全,为什么不安全?有没有比在函数调用时添加临时变量更好、同样简洁的方法来编写它?如果安全,为什么?
4

2 回答 2

15
LegacyFunction(ModernFunction().c_str());

副本的销毁将在评估之后full expression(即从 中返回LegacyFunction)。

n3337 12.2/3

临时对象被销毁作为评估完整表达式(1.9)的最后一步,该完整表达式(在词法上)包含它们被创建的点。

n3337 1.9/10

完整表达式是不是另一个表达式的子表达式的表达式。如果语言构造被定义为产生函数的隐式调用,则语言构造的使用被认为是用于此定义目的的表达式。在临时对象以外的对象的生命周期结束时生成的对析构函数的调用是隐式完整表达式。为了满足表达式出现的语言结构的要求而应用于表达式结果的转换也被认为是完整表达式的一部分。[ 例子:

struct S {
S(int i): I(i) { }
int& v() { return I; }
private:
int I;
};
S s1(1); // full-expression is call of S::S(int)
S s2 = 2; // full-expression is call of S::S(int)
void f() {
if (S(3).v()) // full-expression includes lvalue-to-rvalue and
// int to bool conversions, performed before
// temporary is deleted at end of full-expression
{ }
}

于 2012-09-07T17:57:05.633 回答
9

ModernFunction 返回后存在一个临时字符串对象。什么时候超出范围?

严格来说,它永远不在范围内。范围是名称的属性,而不是对象。碰巧自动变量在作用域生命周期之间有着非常密切的关联。不是自动变量的对象是不同的。

临时对象在它们出现的完整表达式结束时被销毁,有几个例外在这里不相关。无论如何,特殊情况会延长临时的生命周期,他们不会减少它。

编译器是否可以调用 c_str(),破坏这个临时字符串对象,然后将悬空指针传递给 LegacyFunction

不,因为完整表达式是LegacyFunction(ModernFunction().c_str())(不包括分号:feel that pedantry),所以作为返回值的临时值在返回ModernFunction之前不会被破坏LegacyFunction

如果安全,为什么?

因为临时的生命周期足够长。

一般来说c_str,你必须担心两件事。首先,如果字符串被破坏(这就是您要问的),它返回的指针将变得无效。其次,如果字符串被修改,它返回的指针将变得无效。您在这里并不担心,但没关系,您不需要,因为也没有任何东西修改字符串。

于 2012-09-07T18:01:32.443 回答