1

在此处查看此代码:

class test
{
    int n;
    int *j;
public:
    test(int m)
    {
        n = 12;
        j = new int; 
        cin >> *j;
    }

    void show()
    {
        cout << n << ' ' << *j << endl;
    }

    ~test()
    {
        delete j;
    }

};

int main()
{
    test var = 123;
    var.show();
    return 0;
}

在这个程序中,编译器应该抱怨重复删除j. 第一次删除是在临时对象temporary(123)被销毁时完成的。第二次删除是在var对象被销毁时完成的。但这工作正常吗?

这是否意味着临时对象不调用析构函数?

4

3 回答 3

8

有争议的路线是这样的:

test var = 123;

我相信相关的标准文本(评论中的专家正在引用)是(8.5,“声明符”):

以初始化表达式作为参数调用所选函数;如果函数是构造函数,则调用初始化目标类型的 cv 非限定版本的临时版本。临时值是一个右值。然后根据上述规则,调用的结果(对于构造函数的情况是临时的)用于直接初始化作为复制初始化目标的对象。在某些情况下,允许实现通过将中间结果直接构造到正在初始化的对象中来消除这种直接初始化中固有的复制

事实上,在 12.6 中,我们得到了一个这样的例子:

complex f = 3;  // construct complex(3) using
                // complex(double)
                // copy it into f

因此,在您使用 时=,您的实现可能是直接构造对象并完全消除中间临时对象(并且,正如评论所指出的,大多数都这样做)。

此类没有正确复制,因此创建它的副本(以及释放副本和原始类)将导致双重删除(以及崩溃、未定义的行为等)。因为没有创建副本,所以上面不会发生这种情况。

于 2012-01-25T08:14:08.110 回答
2

两点:首先,在这种特殊情况下,允许编译器通过标准的明确授权来优化临时性。我熟悉的所有编译器都可以。您可以通过定义复制构造函数并对其进行检测来验证代码中是否发生了这种情况。

其次是如果临时没有优化出来,你的代码有未定义的行为。双重删除可以有任何可以想象的行为:立即崩溃,可用空间领域的损坏(如果程序继续运行会导致更晚的崩溃),没有任何影响,或者其他任何事情。您没有看到任何症状这一事实并不意味着代码是正确的。

于 2012-01-25T08:30:56.610 回答
1

代码碰巧没有崩溃的事实并不意味着它是正确的。

您的课程有问题,因为它很容易deletes以您描述的方式翻倍。

例如,更改var.show();为以下内容:

    test(var).show();

使代码在我的计算机上可靠地爆炸。

要解决此问题,请实现复制构造函数和赋值运算符。

于 2012-01-25T08:07:37.987 回答