C++ RAII 不工作?

发布于 2024-07-26 01:57:08 字数 865 浏览 9 评论 0原文

我刚刚开始使用 C++ 中的 RAII 并设置了一个小测试用例。 要么我的代码非常混乱,要么 RAII 不起作用! (我猜是前者)。

如果我运行:

#include <exception>
#include <iostream>
class A {
public:
    A(int i) { i_ = i; std::cout << "A " << i_ << " constructed" << std::endl; }
    ~A() { std::cout << "A " << i_ << " destructed" << std::endl; }
private:
    int i_;
};

int main(void) {
    A a1(1);
    A a2(2);
    throw std::exception();
    return 0;
}

并注释掉异常,我会得到:

A 1 constructed
A 2 constructed
A 2 destructed
A 1 destructed

正如预期的那样,但除了异常之外,我得到:

A 1 constructed
A 2 constructed
terminate called after throwing an instance of 'std::exception'
  what():  std::exception
Aborted

所以我的对象不会被破坏,即使它们超出了范围。 这不是 RAII 的全部基础吗?

非常感谢指点和更正!

I'm just getting started with RAII in C++ and set up a little test case. Either my code is deeply confused, or RAII is not working! (I guess it is the former).

If I run:

#include <exception>
#include <iostream>
class A {
public:
    A(int i) { i_ = i; std::cout << "A " << i_ << " constructed" << std::endl; }
    ~A() { std::cout << "A " << i_ << " destructed" << std::endl; }
private:
    int i_;
};

int main(void) {
    A a1(1);
    A a2(2);
    throw std::exception();
    return 0;
}

with the exception commented out I get:

A 1 constructed
A 2 constructed
A 2 destructed
A 1 destructed

as expected, but with the exception I get:

A 1 constructed
A 2 constructed
terminate called after throwing an instance of 'std::exception'
  what():  std::exception
Aborted

so my objects aren't destructed even though they are going out of scope. Is this not the whole basis for RAII.

Pointers and corrections much appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

帝王念 2024-08-02 01:57:08

您没有异常处理程序。 当发生这种情况时,标准表示调用 std::terminate,而 std::terminate 又调用 abort。 请参阅《C++ 编程语言》第 3 版第 14.7 节。

You don't have a handler for your exception. When this happens the standard says that std::terminate is called, which in turn calls abort. See section 14.7 in The C++ Programming Language, 3rd edition.

过期情话 2024-08-02 01:57:08

问题是 main 具有特殊的地位。 当从那里抛出异常时,堆栈无法有意义地展开,应用程序只是调用 std:terminate 来代替。

那么为什么变量不会超出范围就有点道理了。 我们实际上并没有离开它们声明的范围。 发生的情况可以被认为与此相同:(

int main(void) {
  A a1(1);
  A a2(2);
  std::terminate();
}

我相信在这种情况下是否调用析构函数是实现定义的,因此在某些平台上,它会按您的预期工作)

The problem is that main has a special status. When an exception is thrown from there, the stack can't be meaningfully unwound, the application just calls std:terminate instead.

And then it makes a bit of sense why the variables don't go out of scope. We haven't actually left the scope in which they were declared. What happens could be considered to be equivalent to this:

int main(void) {
  A a1(1);
  A a2(2);
  std::terminate();
}

(I believe it is implementation-defined whether destructors are called in this case though, so on some platforms, it'll work as you expected)

望她远 2024-08-02 01:57:08

您在 main 中有一个未处理的异常,这意味着调用终止。 尝试这个:

int main(void)
{
    try
    {
        A a1(1);
        A a2(2);
        throw std::exception();
        return 0;
    }
    catch(const std::exception & e)
    {
        return 1;
    }


}

You have an unhanded exception in the main, which means a call to terminate. Try this:

int main(void)
{
    try
    {
        A a1(1);
        A a2(2);
        throw std::exception();
        return 0;
    }
    catch(const std::exception & e)
    {
        return 1;
    }


}
离不开的别离 2024-08-02 01:57:08

如果异常从 main() 中逃脱,则这是实现定义的天气堆栈被展开。

尝试

int main()
{
    try
    {
        doWork(); // Do you experiment here. 
    }
    catch(...)
    {   /*
         * By catching here you force the stack to unwind correctly.
         */
        throw;  // re-throw so exceptions pass to the OS for debugging.
    }
}

If an exception escapes main() it is implementation defined weather the stack is unwound.

try

int main()
{
    try
    {
        doWork(); // Do you experiment here. 
    }
    catch(...)
    {   /*
         * By catching here you force the stack to unwind correctly.
         */
        throw;  // re-throw so exceptions pass to the OS for debugging.
    }
}
荆棘i 2024-08-02 01:57:08

正如其他人指出的那样,您遇到了一个未捕获的异常,它调用了 Terminate()。 在这种情况下是否调用析构函数是实现定义的(参见标准,15.3第9段和15.5.1第2段),并且您的实现中的定义显然是“不,他们不会”。 (如果由于抛出没有处理程序的异常之外的任何其他原因调用终止(),则不会调用析构函数。)

As others have pointed out, you've got an uncaught exception, which calls terminate(). It is implementation-defined (see the Standard, 15.3 paragraph 9 and 15.5.1 paragraph 2) whether destructors are called in this case, and the definition in your implementation is apparently "No, they won't". (If terminate() is called for any other reason than throwing an exception that doesn't have a handler, destructors will not be called.)

謸气贵蔟 2024-08-02 01:57:08

您的 A 对象不会被销毁,因为正在调用 std::terminate 。

当未处理的异常从 main 中泄漏时,将调用 std::terminate。 如果您将代码包装在 try/catch 中(即使 catch 只是重新引发),您将看到您期望的行为。

Your A objects are not being destroyed because std::terminate is being called.

std::terminate is called when an unhandled exception leaks out of main. If you wrap your code in a try/catch (even if the catch just re-raises) you'll see the behaviour you were expecting.

梦过后 2024-08-02 01:57:08

您没有正确处理异常,因此您的应用程序在对象超出范围之前退出。

我将再解释一下。 如果异常“冒泡”到主堆栈,则堆栈将被展开(编辑)。 即使将代码移至辅助功能也无法解决此问题。 例如:

      1 #include <exception>
      2 #include <iostream>
      3
      4 void test();
      5
      6 class A {
      7     public:
      8         A(int i) { i_ = i; std::cout << "A " << i_ << " constructed" << std::endl; }
      9         ~A() { std::cout << "A " << i_ << " destructed" << std::endl; }
     10     private:    int i_;
     11 };
     12
     13
     14 int main(void) {
     15     test();
     16     return 0;
     17 }
     18
     19 void test(){
     20             A a1(1);
     21             A a2(2);
     22            throw std::exception();
     23 }

上面的代码不会解决问题。 解决这个问题的唯一方法是将抛出的异常包装在 try-catch 块中。 这将阻止异常到达主线程,并在对象超出范围之前停止发生的终止。

You are not handling the exception properly, so your application is exiting before the objects go out of scope.

I am going to explain a little more. If an exception "bubbles" up to main the stack is unwound (edit). Even moving the code to a secondary function will not fix this issue. ex:

      1 #include <exception>
      2 #include <iostream>
      3
      4 void test();
      5
      6 class A {
      7     public:
      8         A(int i) { i_ = i; std::cout << "A " << i_ << " constructed" << std::endl; }
      9         ~A() { std::cout << "A " << i_ << " destructed" << std::endl; }
     10     private:    int i_;
     11 };
     12
     13
     14 int main(void) {
     15     test();
     16     return 0;
     17 }
     18
     19 void test(){
     20             A a1(1);
     21             A a2(2);
     22            throw std::exception();
     23 }

The above code will not solve the issue. The only way to solve this is to wrap the thrown exception in a try-catch block. This will keep the exception from reaching the main, and stop termination that is happening before the objects go out of scope.

空城缀染半城烟沙 2024-08-02 01:57:08

其他人建议在 main() 中放置一个 try/catch 来处理这个问题,效果很好。 出于某种原因,我发现很少使用的“function-try-block”看起来更好,这让我感到惊讶(我认为它看起来太奇怪了)。 但我不认为有任何真正的优点:

int main(void) 
try
{
    A a1(1);
    A a2(2);
    throw std::exception();
    return 0;
}
catch (...) 
{
    throw;
}

有几个缺点,因为它很少被使用,所以很多开发人员看到它时都会陷入困境,如果考虑到这一点,VC6 就会被它噎住。

Others have suggested putting a try/catch inside main() to handle this, which works fine. For some reason I find the rarely used 'function-try-block' to look better, which surprises me (I thought it would look too weird). But I don't think there's any real advantage:

int main(void) 
try
{
    A a1(1);
    A a2(2);
    throw std::exception();
    return 0;
}
catch (...) 
{
    throw;
}

A couple of disadvantages are that since it's rarely used a lot of developers get thrown for a loop when they see it, and VC6 chokes on it if that's a consideration.

你与昨日 2024-08-02 01:57:08

由于异常在到达 main() 时未得到处理,因此会导致调用 std::terminate(),因此本质上相当于

int main(void) {
  A a1(1);
  A a2(2);
  exit(1);
}

在程序在运行之前终止的情况下不保证调用析构函数超出范围。 对于 RAII 中的另一个漏洞,请考虑:

int main(void) {
  A *a1 = new A(1);
}

Since the exception is not handled by the time it reaches main(), it results in a call to std::terminate(), you essentially have the equivalent of

int main(void) {
  A a1(1);
  A a2(2);
  exit(1);
}

Destructors are NOT guaranteed to be called in cases where the program terminates before they go out of scope. For another hole in RAII, consider:

int main(void) {
  A *a1 = new A(1);
}
温柔少女心 2024-08-02 01:57:08

以下代码有效。

#include <exception> 
#include <iostream> 

class A { 
public: 
    A(int i) { i_ = i; std::cout << "A " << i_ << " constructed" << std::endl; } 
    ~A() { std::cout << "A " << i_ << " destructed" << std::endl; } 
private: 
    int i_; 
}; 

void test() {
    A a1(1); 
    A a2(2); 
    throw std::exception(); 
} 

int main(void) { 
 try {
  test();
 } catch(...) {
 }
    return 0; 
} 

The following code works.

#include <exception> 
#include <iostream> 

class A { 
public: 
    A(int i) { i_ = i; std::cout << "A " << i_ << " constructed" << std::endl; } 
    ~A() { std::cout << "A " << i_ << " destructed" << std::endl; } 
private: 
    int i_; 
}; 

void test() {
    A a1(1); 
    A a2(2); 
    throw std::exception(); 
} 

int main(void) { 
 try {
  test();
 } catch(...) {
 }
    return 0; 
} 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文