【问题标题】:Why throwing exception which is a reference calls copy constructor?为什么抛出引用调用复制构造函数的异常?
【发布时间】:2018-05-16 17:49:26
【问题描述】:

为什么抛出引用调用复制构造函数的异常?

struct Error
{
    Error() {}
    Error(const Error&) = delete;
};

int main()
{
    Error& error = *new Error;

    throw error;
}

编译错误:

error: declared here
     Error(const Error&) = delete;

抛出指针时不会发生:

int main()
{
    Error* error = new Error;

    throw error;
}

没关系。

【问题讨论】:

  • 这些天窥视者非常高兴。好的,所以这些信息可以在其他地方找到 - 但实际上每个问题都是如此。这个介绍得很好,很简洁,误解是可以理解的(笑)。让我们不要那么高兴。
  • @FrançoisAndrieux: throw Error{} 是一个不同的例子,不过,如果我们保持可以“抛出引用”的前提(事实并非如此,这就是答案)。所以,不,这不是“分散注意力”——这是问题的核心。
  • Error error = *new Error 我唯一一次看到这样的代码是有人来自 java,一直写 new。正如其他人建议的那样 throw Error{} 就足够了,但是如果您想预先创建变量,请使用 Error error{}; 即,而不是 Type var = *new Type(arg1, arg2); 您想写 Type var{arg1, arg2};

标签: c++ exception copy-constructor throw


【解决方案1】:

您不能抛出引用。 Throwing always copies the thrown expression value into a special area of storage set aside for thrown objects。否则,您几乎总是会“捕获”一个悬空引用,就像 [理论上] 您的代码中的情况一样。

你的Error类型不能被复制,所以程序是不可能的。

但是,指针当然可以被复制,而最后一个示例中的主要问题是内存泄漏。此外,您的程序将简单地在 throw 语句处终止,因为您没有任何 try/catch

【讨论】:

  • 为什么要悬空?我用'new'创建异常并且从不删除它,所以它应该始终存在。为什么抛出指针应该与抛出引用不同?复制构造函数应该只在按值抛出时调用,而不是引用或指针。
  • @AndreyRubliov:也许我不清楚。悬空引用将在未编译的假设 first 示例中创建。第二个示例反而有内存泄漏。 “复制构造函数应该只在按值抛出时调用,而不是引用或指针。”这根本不是真的,正如我所说的:你总是“按值抛出” , 句号。
【解决方案2】:

在展开堆栈之前,throw 运算符(除了 throw;没有参数,用于重新抛出)创建一个异常对象(在特殊的内存区域中)。根据情况,对象以不同的方式初始化:构造函数、复制构造函数、移动构造函数 (https://en.cppreference.com/w/cpp/language/copy_elision) 使用提供给 throw 运算符的内容。提供参考是可以的,但是三个:

  • 如果您在参数列表中提供引用,则取决于接收方、实际收到的内容、引用或值副本;
  • 编译器需要初始化一个异常对象,因为当异常处理 catch 块运行时,提供给 throw 操作符的内容将不存在(此时堆栈将被展开;如果是指针,则提供的指针虽然在您的情况下它指向的对象是活动的,并且在 catch 块中您有指向同一对象的指针的副本);
  • 无法在运行时初始化引用; 因此,在您的情况下,编译器期望复制构造函数使用您提供的引用来初始化异常对象(复制构造函数通常使用对初始对象的引用来初始化对象)。

当你将Error的引用传递给throw操作符时,异常对象的类型是Error,我们需要在那个特定的内存区域初始化一个Error异常对象..

当你将一个指向Error的指针传递给一个throw操作符时,异常对象的类型是一个指向Error(Error *)的指针,所以复制的是指针,而不是指针指向的Error对象。指向错误的复制指针与调用Error的复制构造函数无关,因此在这种情况下您不会遇到错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-08-12
    • 1970-01-01
    • 2016-08-31
    • 2012-04-24
    • 1970-01-01
    • 2014-08-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多