在构造函数中抛出异常是不安全的吗? [英] Is it ever not safe to throw an exception in a constructor?

查看:210
本文介绍了在构造函数中抛出异常是不安全的吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道从析构函数抛出异常是不安全的,但是从构造函数抛出异常是不安全的吗?

I know that it's not safe to throw exceptions from destructors, but is it ever unsafe to throw exceptions from constructors?

例如。对全局声明的对象会发生什么?一个快速测试与gcc和我得到
中止,总是保证吗?

e.g. what happens for objects that are declared globally? A quick test with gcc and I get an abort, is that always guaranteed? What solution would you use to cater for that situation?

有没有任何情况下,构造函数可以抛出异常,而不会留下我们期望的东西。

Are there any situations where constructors can throw exceptions and not leave things how we expect.

编辑:我想我应该补充说,我想了解在什么情况下我可以得到资源泄露。看来,明智的做法是手动释放资源,我们在抛出异常之前通过构造获得部分路径。我从来没有必要在今天之前在构造函数中抛出异常,所以试图理解是否有任何陷阱。

I guess I should add that I'm trying to understand under what circumstances I could get a resource leak. Looks like the sensible thing to do is manually free up resources we've obtained part way through construction before throwing the exception. I've never needed to throw exceptions in constructors before today so trying to understand if there are any pitfalls.

这也是安全的吗?

class P{
  public:
    P() { 
       // do stuff...

       if (error)
          throw exception
    }
}

dostuff(P *p){
 // do something with P
}

... 
try {
  dostuff(new P())
} catch(exception) {

}

将分配给对象P的内存发布?

will the memory allocated to the object P be released?

EDIT2:忘记提到在这种特殊情况下,dostuff在输出队列中存储对P的引用。 P实际上是一个消息,dostuff接收消息,将其路由到适当的输出队列并发送它。基本上,一旦东西拥有它,它会在后来的东西释放。我想我要把一个autoptr围绕P和调用释放在autoptr后dostuff以防止内存泄漏,这是正确的吗?

Forgot to mention that in this particular case dostuff is storing the reference to P in an output queue. P is actually a message and dostuff takes the message, routes it to the appropriate output queue and sends it. Essentially, once dostuff has hold of it, it gets released later in the innards of dostuff. I think I want to put an autoptr around P and call release on the autoptr after dostuff to prevent a memory leak, would that be correct?

推荐答案

从构造函数中抛出异常是一个好东西。当构造函数失败时,您有两个选项:

Throwing exceptions from a constructor is a good thing. When something fails in a constructor, you have two options:


  • 维护一个zombie状态,

  • 抛出异常。

并且保持僵尸类可能是一个麻烦,

And maintaining zombie classes can be quite a hassle, when the real answer should have been, "this failed, now what?".

根据3.6.2.4的标准:

According to the Standard at 3.6.2.4:


如果非本地静态对象的构造或销毁结束于抛出未捕获异常,则结果是调用terminate(18.6.3.3)。

If construction or destruction of a non-local static object ends in throwing an uncaught exception, the result is to call terminate (18.6.3.3).

其中terminate表示 std: :terminate

Where terminate refers to std::terminate.

这是因为您没有使用 RAII概念。当抛出异常时,堆栈将被解开,这意味着当代码到达最接近的相应的 catch 子句时,所有对象都将调用它的析构函数。

Concerning your example, no. This is because you aren't using RAII concepts. When an exception is thrown, the stack will be unwound, which means all objects get their destructor's called as the code gets to the closest corresponding catch clause.

指针没有析构函数。让我们做一个简单的测试用例:

A pointer doesn't have a destructor. Let's make a simple test case:

#include <string>

int main(void)
{
    try
    {
        std::string str = "Blah.";
        int *pi = new int;

        throw;

        delete pi; // cannot be reached
    }
    catch(...)
    {
    }
}

这里, str 将分配内存,并复制Blah。

Here, str will allocate memory, and copy "Blah." into it, and pi will be initialized to point to an integer in memory.

当引发异常时,会将 pi 初始化为指向内存中的整数。堆叠退绕开始。它将首先调用指针的析构函数(do nothing),然后 str 的析构函数,这将释放分配给它的内存。

When an exception is thrown, stack-unwinding begins. It will first "call" the pointer's destructor (do nothing), then str's destructor, which will free the memory that was allocated to it.

如果您使用RAII概念,您将使用一个智能指针:

If you use RAII concepts, you'd use a smart pointer:

#include <memory>
#include <string>

int main(void)
{
    try
    {
        std::string s = "Blah.";
        std::auto_ptr<int> pi(new int);

        throw;

        // no need to manually delete.
    }
    catch(...)
    {
    }
}

这里, pi 的析构函数会调用 delete ,并且不会泄露内存。这就是为什么你应该总是包装你的指针,这是同样的原因,我们使用 std :: vector ,而不是手动分配,调整大小和释放指针。 (清洁和安全)

Here, pi's destructor will call delete and no memory will be leaked. This is why you should always wrap your pointers, and is the same reason we use std::vector rather than manually allocating, resizing, and freeing pointers. (Cleanliness and Safety)

我忘了提及。你问这个问题:

I forgot to mention. You asked this:


我想我要把一个autoptr放在P上,并在doptuff后的autoptr上调用release来防止内存泄漏,这是正确的吗?

I think I want to put an autoptr around P and call release on the autoptr after dostuff to prevent a memory leak, would that be correct?

我没有明确说明,只是暗示它,但答案是 。所有你需要做的是将它放在 auto_ptr 里,当时间到来,它会被自动删除。

I didn't state it explicitly, and only implied it above, but the answer is no. All you have to do is place it inside of auto_ptr and when the time comes, it will be deleted automatically. Releasing it manually defeats the purpose of placing it in a container in the first place.

我还建议您查看更高级的智能指针,例如 boost 。非常受欢迎的是 shared_ptr ,这是引用计数,使其适合存储在容器中并被复制。 (不同于 auto_ptr 。不要在容器中使用 auto_ptr !)

I would also suggest you look at more advanced smart pointers, such as those in boost. An extraordinarily popular one is shared_ptr, which is reference counted, making it suitable for storage in containers and being copied around. (Unlike auto_ptr. Do not use auto_ptr in containers!)

这篇关于在构造函数中抛出异常是不安全的吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆