为什么要抛出引用调用复制构造函数的异常?
Why throwing exception which is a reference calls copy constructor?
为什么要抛出引用调用复制构造函数的异常?
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;
}
这没关系。
不能抛出引用。抛出始终将抛出的表达式值复制到为抛出的对象预留的特殊存储区域中。否则,你几乎总是会"捕获"一个悬而未决的引用,就像你的代码中[理论上]的情况一样。
您的Error
类型无法复制,因此该程序是不可能的。
但是,指针当然可以复制,最后一个示例中的主要问题是内存泄漏。此外,您的程序将简单地在throw
语句处终止,因为您没有任何try
/catch
。
在展开堆栈之前,抛出运算符(抛出除外;没有参数,用于重新抛出)创建一个异常对象(在特殊的内存区域中)。根据情况,对象以不同的方式初始化:构造函数、复制构造函数、移动构造函数 (https://en.cppreference.com/w/cpp/language/copy_elision) 使用提供给 throw 运算符的内容。提供参考是可以的,但有三个:
- 如果您在参数列表中提供参考,这取决于接收方、实际收到的内容、参考或价值副本;
- 编译器需要初始化一个异常对象,因为当异常处理 catch 块运行时,提供给 throw 运算符的内容将不会存在(然后堆栈将被展开;如果是指针,则提供指针,尽管它指向的对象在您的情况下是活动的,并且在 catch 块中您有指向同一对象的指针的副本); 无法
- 在运行时初始化引用; 因此,在您的情况下,编译器需要复制构造函数,以便使用您提供的引用初始化异常对象(复制构造函数通常使用对初始对象的引用来初始化对象)。
当您将对 Error 的引用传递给 throw 运算符时,异常对象的类型为 Error,我们需要在该特定内存区域中初始化一个 Error 异常对象。
将指向 Error 的指针传递给抛出运算符时,异常对象的类型是指向 Error (Error *) 的指针,因此将复制指针,而不是指针指向的 Error 对象。指向 error 的复制指针与调用 Error 的复制构造函数无关,因此在这种情况下您没有错误。
相关文章:
- 从构造函数抛出异常时如何克服内存泄漏
- 如何编写带有异常的构造函数
- 从 C++ 中异常的构造函数引发异常
- 我正在尝试创建一个使用 c++ 中的参数包构造函数的异常类
- 稍后在构造函数中重新启动异常指令删除此指令
- 自定义异常中的用户定义的空构造函数,具有多个继承和抽象基类
- 构造函数中引发的异常
- 如何捕获源自静态分配对象的构造函数的异常?
- 从构造函数内存泄漏引发异常
- 智能指针和构造函数异常
- 构造函数C++异常说明符
- boost::archive::text_iarchive构造函数异常
- C++堆栈对象的构造函数异常处理
- 默认构造函数C++异常不会引发吗?
- 当函数中静态变量的构造函数异常终止时会发生什么
- 在c++中,如果基类构造函数异常,则构造函数和析构函数的顺序可以是这样
- 如何捕获构造函数异常
- C++构造函数异常处理
- 捕获构造函数异常的RAII方法
- Std::unique_ptr::reset和构造函数异常