提问人:Zebrafish 提问时间:9/27/2017 更新时间:9/28/2017 访问量:121
我无法理解此复制构造函数行为
I can't understand this copy constructor behaviour
问:
我有一些奇怪的行为,如下:
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;
}
当我制作自己的复制构造函数时,无论它是否采用常量,“other”参数中的值都是垃圾。如果复制构造函数是默认构造函数,则我不会出现这种行为。我使用 GCC 在 IDEOne 上尝试过,但此代码无法编译。但是,在我的 Visual Studio 上,它按照我的描述运行。
我发现很难理解临时有效期的规则。例如,我认为如果 getNumber() 返回对本地临时的引用,那么直接在同一行上分配它是可以的。我错了。
答:
该函数创建一个临时函数并返回对它的引用。临时对象在函数结束时不复存在,这意味着您现在返回的引用是指已销毁的对象。这是未定义的行为,这意味着该行为可以是任何东西,包括有时看起来有效。编译器不需要诊断此错误,但某些编译器(如 GCC)需要诊断此错误。如果要返回对共享实例的可变引用,请在函数正文中声明一个静态本地对象,并返回对它的引用。static Number& getNumber() { return Number(555); }
Number
static Number& getNumber()
{
static Number my_instance{555};
return my_instance;
}
getNumber
具有未定义的行为。您正在返回对本地对象的引用。当函数返回该对象时,该对象被销毁,因此现在您具有对不再存在的对象的引用。为了解决这个问题,我们可以按值返回,例如
static Number getNumber() { return {555}; }
现在,返回的数字是直接从返回值构造的。
在创建返回值之后,但在执行继续之前,所有函数局部变量都会被销毁。这意味着返回任何类型的引用或指向本地对象的指针都会给你留下一个悬空的引用/指针。
评论
num2
我学到了一些东西来试图回答这个问题。
从静态函数返回对象的可能方法有哪些?
按值
这通常是正确的方法。通常,编译器会使用 Return-Value-Optimisation 或以其他方式避免多次实际复制对象。如果您不确定这可能是您想要的。
如果默认的复制构造函数对你来说还不够,请确保你定义了自己的构造函数,记住要定义它以采用 const ref 参数。如果您使用的是 C++ 11,则定义单独的移动构造函数可能很有用。
通过非常量引用
这是不正确的,因为它是指向内存位置的引用(实际上是指针),该位置曾经包含不再存在的变量。
这是 gcc 中的错误。它曾经在 Visual Studio 中被允许,尽管我听说它可能不再允许了。如果需要,可以使用编译器选项 /Za 进行编译,以关闭各种特定于 Microsoft 的扩展。
通过常量引用
这不是错误,而是警告,是未定义的行为 [需要引证]
您可以将 const ref 绑定到临时对象。参见 Herb Sutter 的文章:https://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/
例如,“const Number&num = get_number_by_value()”通常会返回一个临时对象的副本,并省略该副本,然后临时对象将被绑定到引用,并以专门用于 const ref(但不是其他引用或指针)的方式延长其生存期。
但是,我现在才知道,从技术上讲,这适用于从函数返回临时函数,但如果将其分配给另一个常量引用,则该生命周期不会进一步延长。
所以你的情况
Number num = get_number_by_const_ref()
可能工作正常,但
const Number& num = get_number_by_const_ref()
可能不会。
返回对静态成员变量的常量引用
这通常没有帮助,但是如果你的类的构造成本非常高(需要大量计算或使用 GB 内存),并且你想多次返回它的特定实例,你可能有一个类的私有 const static 成员变量,它存储一个你可以通过 ref 返回的实例。
请记住,如果您有一个包含类的实例变量的静态成员变量,则需要在类外部的 .c 文件中对其进行初始化,以便构造函数可用。
评论
/Za
Project->Properties->C/C++->Language->Disable Language Extensions->Yes
Number