gpt4 book ai didi

c++ - 在构造函数中抛出异常时未调用析构函数

转载 作者:可可西里 更新时间:2023-11-01 17:01:05 26 4
gpt4 key购买 nike

为什么在这段代码中没有调用析构函数?

#include <boost/scoped_ptr.hpp>
#include <iostream>

class MyClass {
boost::scoped_ptr<int> ptr;
public:
MyClass() : ptr(new int) { *ptr = 0; throw; std::cout<<"MyClass Allocated\n"; }
~MyClass() { std::cout<<"MyClass De-allocated\n"; }
int increment() { return ++*ptr; }
};

int main()
{
boost::scoped_ptr<MyClass> myinst(new MyClass);
std::cout << myinst->increment() << '\n';
std::cout << myinst->increment() << '\n';
}

编辑

从答案中了解到,当构造函数中发生异常时,不会调用析构函数。但是如果异常发生在main()中,即MyClass对象完全实例化后,MyClass的析构函数会被调用吗?如果不是,那为什么它是智能指针?

添加代码

#include <boost/scoped_ptr.hpp>
#include <iostream>

class MyClass {
boost::scoped_ptr<int> ptr;
public:
MyClass() : ptr(new int) { *ptr = 0; std::cout<<"MyClass Allocated\n"; }
~MyClass() { std::cout<<"MyClass De-allocated\n"; }
int increment() { return ++*ptr; }
};

int main()
{
boost::scoped_ptr<MyClass> myinst(new MyClass);
throw 3;
std::cout << myinst->increment() << '\n';
std::cout << myinst->increment() << '\n';
}

输出:

MyClass Allocated
terminate called after throwing an instance of 'int'
Aborted

最佳答案

C++ 对象的生命周期仅在其构造函数成功完成后才开始。
由于在构造函数调用完成之前抛出异常,因此您没有完整的对象,因此没有析构函数。

赫伯萨特 explains this nicely ,引用他的话:

Q: What does emitting an exception from a constructor mean?

A: It means that construction has failed, the object never existed, its lifetime never began. Indeed, the only way to report the failure of construction -- that is, the inability to correctly build a functioning object of the given type -- is to throw an exception. (Yes, there is a now-obsolete programming convention that said, "if you get into trouble just set a status flag to 'bad' and let the caller check it via an IsOK() function." I'll comment on that presently.)

In biological terms,
conception took place -- the constructor began -- but despite best efforts it was followed by a miscarriage -- the constructor never ran to term(ination).

Incidentally, this is why a destructor will never be called if the constructor didn't succeed -- there's nothing to destroy. "It cannot die, for it never lived." Note that this makes the phrase "an object whose constructor threw an exception" really an oxymoron. Such a thing is even less than an ex-object... it never lived, never was, never breathed its first. It is a non-object.

We might summarize the C++ constructor model as follows:

Either:

(a) The constructor returns normally by reaching its end or a return statement, and the object exists.

Or:

(b) The constructor exits by emitting an exception, and the object not only does not now exist, but never existed as an object.

编辑 1:
但如果异常发生在main() , 即在 MyClass 之后对象已完全实例化,将 MyClass调用析构函数?

是的,会的!
这就是使用 scoped_ptr 的目的。 ,一旦在main中抛出异常, Stack Unwinding 会导致所有本地对象被释放,这意味着 myinst (驻留在堆栈上)也将被释放,这又将调用 MyClass 的析构函数.

引用 Boost doccumentation 有疑问时:

The scoped_ptr class template stores a pointer to a dynamically allocated object. (Dynamically allocated objects are allocated with the C++ new expression.) The object pointed to is guaranteed to be deleted, either on destruction of the scoped_ptr, or via an explicit reset

编辑 2:
为什么你编辑的程序会崩溃?
您的程序显示崩溃是因为,您抛出异常但从未捕获到它。当这种情况发生时,一个名为 terminate() 的特殊函数被调用,其默认行为是调用 abort() .堆栈是否在terminate()之前展开是实现定义的行为在此特定场景中被调用Ref 1。似乎您的实现没有并且您也不应该依赖此行为。

您可以按如下方式修改您的程序来处理异常,您应该会得到您期望的行为:

#include <boost/scoped_ptr.hpp> 
#include <iostream>

class MyClass {
boost::scoped_ptr<int> ptr;
public:
MyClass() : ptr(new int) { *ptr = 0; std::cout<<"MyClass Allocated\n"; }
~MyClass() { std::cout<<"MyClass De-allocated\n"; }
int increment() { return ++*ptr; }
};

void doSomething()
{
boost::scoped_ptr<MyClass> myinst(new MyClass);
throw 3;
}

int main()
{
try
{
doSomething();
}
catch(int &obj)
{
std::cout<<"Exception Handled";
}

}

引用 1 C++03 15.5.1 terminate()函数

In the following situations exception handling must be abandoned for less subtle error handling techniques:
....
— when the exception handling mechanism cannot find a handler for a thrown exception (15.3),
....

In such cases,

  1. void terminate();

is called (18.6.3). In the situation where no matching handler is found, it is implementation-defined whether or not the stack is unwound before terminate() is called. In all other situations, the stack shall not be unwound before terminate() is called. An implementation is not permitted to finish stack unwinding prematurely based on a determination that the unwind process will eventually cause a call to terminate().

关于c++ - 在构造函数中抛出异常时未调用析构函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9971782/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com