C++ 我能';我不理解这个复制构造函数的行为

C++ 我能';我不理解这个复制构造函数的行为,c++,reference,copy-constructor,lifetime,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; //}

我在以下方面有一些奇怪的行为:

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;
}
使用名称空间std;
结构编号
{
数字(int init):a(init){
Number(){};
INTA;
静态编号&getNumber(){返回编号(555);}
//编号(常数编号和其他)
//{
//a=其他。a;
//}//我已经注释掉了,你会明白原因的
};
int main()
{
数字num1;//现在是垃圾
num2;//现在是垃圾
num2=Number::getNumber();//num 2仍然是垃圾
num3=Number::getNumber();//num3已分配555。
//如果我定义自己的复制构造函数
//(取消注释),它仍然是垃圾。

cout函数
static Number&getNumber(){returnnumber(555);}
创建一个临时的
编号
并返回对该编号的引用。临时对象在函数末尾不再存在,这意味着您现在返回的引用引用引用了一个已销毁的对象。这是未定义的行为,这意味着该行为可以是任何行为,包括有时看起来有效。编译器不可用需要诊断此错误,但某些(如GCC)需要。如果要返回对共享实例的可变引用,请在函数体中声明一个静态本地对象并返回对它的引用

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

getNumber
具有未定义的行为。您正在返回一个对本地对象的引用。当函数返回该对象时,该对象已被销毁,因此现在您有一个对不再存在的对象的引用。要解决此问题,我们只需按如下值返回

static Number getNumber() { return {555}; }
现在,返回的数字是直接从返回值构造的


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

我在尝试回答这个问题时学到了一些东西

从静态函数返回对象的可能方式有哪些

按值计算

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

如果默认的复制构造函数不足以满足您的需要,请确保您定义了自己的复制构造函数,记住将其定义为一个const ref参数。如果您使用的是C++11,则定义一个单独的移动构造函数可能会很有用

通过非常量引用

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

这是gcc中的一个错误。在Visual Studio中过去是允许的,但我听说可能不再允许。如果需要,可以使用编译器选项/Za进行编译以关闭各种microsoft特定的扩展

按常量引用

这不是错误,而是警告,是未定义的行为[需要引用]

您可以将const ref绑定到临时对象。请参阅Herb Sutter的文章:

e、 g.“const Number&num=get_Number_by_value()”通常会返回临时对象的副本,并删除该副本,然后临时对象将绑定到引用,并以专门针对const ref(但不是其他ref或指针)的方式延长其生存期

然而,我现在刚刚了解到,从技术上来说,这适用于从函数返回一个临时值,但如果将其分配给另一个const ref,则该生存期不会进一步延长

那你的案子呢

Number num = get_number_by_const_ref()
也许可以,但是

const Number& num = get_number_by_const_ref()
可能不会

返回对静态成员变量的常量引用

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


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

您的静态函数通过引用返回一个临时对象。因此,它在使用前会被销毁。如果gcc不编译,请停止并倾听其错误或者消息。MSVS有一个允许编译的文件。@NathanOliver这真是邪恶,我想要这个warning@Zebrafish:使用
/Za
标志。如果您在IDE中,请转到主菜单
Project->Properties->C/C++->Language->Disable Language Extensions->Yes
,对于像
Number
这样的简单值类型,通常您应该通过值而不是引用来遍历它们。(注意复制构造函数必须通过引用获取其参数;否则将得到无限递归)不,我知道num2在这种情况下使用赋值运算符。所以,我知道临时本地会被销毁,但我认为如果它直接在同一行上赋值,它会赋值正确。我错了?换句话说,本地在返回的那一刻就不好,无论它是否被赋值。@Zebrafish是的。函数ion local arguments会在获得要分配给
num2
的引用后立即销毁。在表达式中创建的临时值会一直持续到完整表达式结束,但不包括函数本地参数。这些临时值总是在函数返回时和执行前销毁。您无法重新创建通过const ref打开一个函数局部变量谢谢。这是一个错误。你知道引用标准的哪一部分吗?不是马上,它包含在范围和生存期规则中。不过这是一篇关于它的好文章。