const引用类成员是否延长了临时生命?

时间:2010-05-06 20:31:50

标签: c++ temporary ctor-initializer const-reference

为什么这样:

#include <string>
#include <iostream>
using namespace std;

class Sandbox
{
public:
    Sandbox(const string& n) : member(n) {}
    const string& member;
};

int main()
{
    Sandbox sandbox(string("four"));
    cout << "The answer is: " << sandbox.member << endl;
    return 0;
}

输出:

  

答案是:

而不是:

  

答案是:四个

7 个答案:

答案 0 :(得分:152)

只有本地 const引用会延长使用寿命。

该标准在§8.5.3/ 5 [dcl.init.ref]中指定了这种行为,这是关于参考声明的初始化者的部分。示例中的引用绑定到构造函数的参数n,并且当对象n绑定超出范围时变为无效。

生命周期扩展不能通过函数参数传递。 §12.2/ 5 [class.temporary]:

  

第二个上下文是引用绑定到临时的。除了下面指定的内容之外,引用绑定的临时对象或作为临时绑定对象的子对象的完整对象的临时对象的生命周期仍然存在。绑定到构造函数的ctor-initializer(第12.6.2节[class.base.init])中的引用成员的临时绑定将持续存在,直到构造函数退出。在函数调用(第5.2.2节[expr.call])中对引用参数的临时绑定将持续存在,直到包含该调用的完整表达式完成为止。

答案 1 :(得分:23)

这是解释发生事情的最简单方法:

在main()中,您创建了一个字符串并将其传递给构造函数。此字符串实例仅存在于构造函数中。在构造函数中,您指定成员直接指向此实例。当scope离开构造函数时,字符串实例被销毁,然后成员指向不再存在的字符串对象。让Sandbox.member指向其范围之外的引用将不会将这些外部实例保留在范围内。

如果要修复程序以显示所需的行为,请进行以下更改:

int main()
{
    string temp = string("four");    
    Sandbox sandbox(temp);
    cout << sandbox.member << endl;
    return 0;
}

现在temp将在main()的末尾而不是在构造函数的末尾超出范围。但是,这是不好的做法。您的成员变量永远不应该是对实例外部存在的变量的引用。在实践中,你永远不知道该变量什么时候会超出范围。

我建议将Sandbox.member定义为const string member;这会将临时参数的数据复制到成员变量中,而不是将成员变量指定为临时参数本身。

答案 2 :(得分:4)

从技术上讲,这个程序不需要实际输出任何标准输出(这是一个缓冲流开始)。

  • cout << "The answer is: "位会将"The answer is: "发送到stdout的缓冲区

  • 然后<< sandbox.member位会将悬空引用提供给operator << (ostream &, const std::string &),这会调用未定义的行为

因此,没有任何保证会发生。该程序可能看起来很好,甚至可能会崩溃,甚至没有刷新标准 - 这意味着文本“答案是:”不会出现在你的屏幕上。

答案 3 :(得分:0)

因为一旦Sandbox构造函数返回,你的临时字符串就会超出范围,并且为了其他目的而回收它所占用的堆栈。

通常,您不应长期保留参考文献。引用适用于参数或局部变量,而不是类成员。

答案 4 :(得分:0)

答案很清楚:你指的是已经消失的东西。以下将起作用

#include <string>
#include <iostream>
using namespace std;

class Sandbox
{

public:
    const string member = " ";
    Sandbox(const string& n) : member(n) {}//a copy is made

};

int main()
{
    Sandbox sandbox(string("four"));
    cout << "The answer is: " << sandbox.member << endl;
    return 0;
}

答案 5 :(得分:0)

您指的是已经消失的东西。以下将起作用

#include <string>
#include <iostream>

class Sandbox
{

public:
    const string member = " "; //default to whatever is the requirement
    Sandbox(const string& n) : member(n) {}//a copy is made

};

int main()
{
    Sandbox sandbox(string("four"));
    std::cout << "The answer is: " << sandbox.member << std::endl;
    return 0;
}

答案 6 :(得分:0)

从其他答案中可以清楚地看出,类成员不会延长构造函数调用之外的临时对象的生命周期。但在某些情况下,您的 API 可以“安全地”假设传递给类的所有 async upload(image) { return new Promise((resolve, reject) => { console.log(image) var s3 = new aws.S3({ accessKeyId: environment.ID, secretAccessKey: environment.SECRET, params: { Bucket: environment.S3.BUCKET_NAME } }); let buf = new Buffer(image.image, "base64"); console.log(buf,"buffer"); var data = { Bucket: environment.S3.BUCKET_NAME, Key: image.filepath, Body: buf, ContentEncoding: "base64", ContentType: "image/jpeg" }; s3.upload(data, (err, res) => { if (err) { reject(err); console.log("Reject",err); } //console.log(`File uploaded successfully. ${res.Location}`); else { resolve(res); } }); }); } 对象不是临时对象,而是对范围良好的对象的引用。

如果您不想创建副本,您可以做些什么来确保 UB 不会潜入您的代码中?您拥有的最佳工具是通过将接受此类临时文件的重载声明为已删除来保护传递给构造函数的 const& 不是临时文件的假设:

std::string const&

Demo