被引用捕获的抛出对象的生命周期

3

C++标准中第15.1.4段规定:

抛出异常时临时副本的内存分配方式未指定,除3.7.3.1中所述外。 只要有处理程序在执行该异常,临时副本就会存在。

我想知道为什么这段代码会崩溃(我知道这不是最佳实践):

class magicException
{
private:
    char* m_message;

public:
    magicException(const char* message)
    {
        m_message = new char[strlen(message) + 1];
        strcpy(m_message, message);
    }

    ~magicException()
    {
        cout << "Destructor called." << endl;
        delete[] m_message;
    }

    char* getMessage()
    {
        return m_message;
    }
};

void someFunction()
{
    throw magicException("Bang!");
}

int main(int argc, char * argv[])
{
    try
    {
        someFunction();
    }
    catch (magicException& ex)
    {
        cout << ex.getMessage() << endl;
    }

    return 0;
}

具体来说,在 catch 块之前,会调用抛出的 magicException 对象的析构函数。但是,如果我向我的类中添加一个复制构造函数:
magicException(const magicException& other)
{
    cout << "Copy constructor called." << endl;
    m_message = new char[strlen(other.m_message) + 1];
    strcpy(m_message, other.m_message);
}

代码运行良好,析构函数在预期的位置被调用(即catch块的末尾),但有趣的是复制构造函数仍未被调用。它是否被编译器优化掉了(使用关闭优化的Visual C++ 2008编译),还是我漏掉了什么?


也许编译器最初看到复制构造函数是平凡的,并决定使用它。当您添加了一个用户定义的复制构造函数时,它可能意识到将调用省略为优化可能更好。无论如何,即使编译器优化了副本,您仍必须遵循三个规则。 - UncleBens
对我来说没有崩溃,实际上那段代码没有错误。你的编译器必须处理异常对象的生命周期,你的代码是按照规则编写的。 !!!!!!!!!! 所以换个编译器吧 !!!!!!!!!! - erick2red
1个回答

4

具体来说,在catch块之前,抛出的magicException对象的析构函数会被调用。

是的,正如您从标准引用中所引用的那样,编译器会复制并且原始副本(可能)被丢弃。您的问题在于原始代码缺少一个拷贝构造函数。然而,C++编译器可以在各种情况下删除(或添加)拷贝构造函数调用,包括此情况。


我已经添加了一个复制构造函数(请参见我的帖子),但它没有被调用。我的问题是,为什么复制构造函数没有被调用。 - Stefan Schultze
请看我的编辑 - 即使具有副作用,编译器也可能省略复制构造函数的调用。 - anon
谢谢,我找到了一篇澄清这个问题的文章:http://en.wikipedia.org/wiki/Return_value_optimization#Other_forms_of_copy_elision - Stefan Schultze
1
@Stefan:使用 -fno-elide-constructors(g++)关闭该优化功能。 - Prasoon Saurav
1
@Prasoon 他的问题说他正在使用VC++。 - anon
1
@Neil:MSVC++中是否有类似的命令可以关闭该功能? - Prasoon Saurav

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接