我无法理解这个复制构造函数的行为

时间:2017-09-27 13:38:50

标签: c++ reference copy-constructor lifetime

我对以下内容有一些奇怪的行为:

using namespace std;
struct Number
{
    Number(int init) : a(init) {}
    Number() {};
    int a;
    static Number& getNumber() { return Number(555); }

    //Number(const Number& other)
    //{
    //  a = other.a;
    //}  // I've commented this out you'll see why
};

int main()
{
    Number num1;  // Is now junk
    Number num2;  // Is now junk
    num2 = Number::getNumber(); // num 2 is still junk

    Number num3 = Number::getNumber(); // num3 has been assigned 555. 
                                       // If I define my own copy constructor
                                       // (uncomment it), it stays junk.
    cout << num3.a;
}

当我创建自己的复制构造函数时,无论是否需要const,值都会出现在&#34;其他&#34;争论是垃圾。如果复制构造函数是默认构造函数,我不会得到这种行为。我在IDEOne上使用GCC尝试过,这段代码没有编译。但是在我的Visual Studio上它按照我的描述运行。

我发现很难理解临时有效期的规则。例如,我认为如果getNumber()返回对本地临时的引用,如果它直接在同一行上分配,则表示没问题。我错了。

3 个答案:

答案 0 :(得分:2)

getNumber有未定义的行为。您将返回对本地对象的引用。当函数返回时,该对象被销毁,所以现在您有一个不再存在的对象的引用。要解决这个问题,我们可以像

一样按值返回
static Number getNumber() { return {555}; }

现在您返回的数字直接由返回值构成。

在创建返回值之后但在执行之前销毁所有函数局部变量。这意味着返回任何类型的引用或指向本地对象的指针将为您留下悬空引用/指针。

答案 1 :(得分:1)

我学到了一些东西试图回答这个问题。

从静态函数返回对象的可能方法是什么?

按价值

这通常是正确的方法。通常编译器将使用返回值优化或以其他方式避免实际复制对象多次。如果您不确定这可能是您想要的。

如果默认的复制构造函数不适合您,请确保定义自己的构造函数,记住定义它以获取const ref参数。如果你正在使用C ++ 11,那么定义一个单独的移动构造函数可能会很有用。

通过非const引用

这是不正确的,因为它是一个内存位置的引用(实际上是一个指针),用于包含一个不再存在的变量。

这是gcc中的一个错误。它曾经被允许在Visual Studio中,虽然我听说它可能不再存在。如果需要,可以使用编译器选项/ Za进行编译以关闭各种特定于Microsoft的扩展。

通过const引用

这不是错误,而是一个警告,并且是未定义的行为[需要引证]

可以将const引用绑定到临时对象。参见Herb Sutter的文章:https://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/

e.g。 “const Number&amp; num = get_number_by_value()”通常会返回临时对象的副本,并删除副本,然后临时对象将绑定到引用,并将其生命周期扩展为专门用于const ref的方式(但不是其他参考或指针)。

但是,我刚刚学会了查看,这在技术上适用于从函数返回一个临时函数,但是如果将其赋值给另一个const ref,则生命周期不会进一步延长。 < / p> <击>

所以你的情况

Number num = get_number_by_const_ref()

可能正常但

const Number& num = get_number_by_const_ref()

可能不会。

将const引用返回给静态成员变量

这通常没有用,但是如果您的类构造起来非常昂贵(需要大量计算或使用GB内存)并且您希望多次返回它的特定实例,那么您可能有一个私有的const static存储可以通过ref返回的实例的类的成员变量。

请记住,如果你有一个包含该类的实例变量的静态成员变量,则需要在.c文件中的外部类初始化,以便构造函数可用。

答案 2 :(得分:0)

函数static Number& getNumber() { return Number(555); }创建一个临时Number并返回对它的引用。临时对象不再存在于函数的末尾,这意味着您现在返回的引用是指被销毁的对象。这是未定义的行为,这意味着行为可能是任何事情,包括有时候出现在工作中。编译器不需要诊断此错误,但有些(例如GCC)会这样做。如果您打算将可变引用返回给共享实例,请在函数体中声明一个静态本地对象并返回对它的引用。

static Number& getNumber() 
{ 
    static Number my_instance{555}; 
    return my_instance;
}