C++ 使_在try-catch-scoping问题中共享

C++ 使_在try-catch-scoping问题中共享,c++,C++,我需要分配内存,但我想在try/catch中这样做,但这引入了一个新的作用域,在这个作用域中,一旦我超出try作用域,变量就不可用。解决这个问题的最好办法是什么 try { auto something = std::make_unique<SomeClass>; } catch (std::bad_alloc) { ... } // ... lots of code I don't want to include in the try/catch-scope. s

我需要分配内存,但我想在try/catch中这样做,但这引入了一个新的作用域,在这个作用域中,一旦我超出try作用域,变量就不可用。解决这个问题的最好办法是什么

try {
    auto something = std::make_unique<SomeClass>;
} 
catch (std::bad_alloc) {
    ...
}
// ... lots of code I don't want to include in the try/catch-scope.
something.callSomeMethod();

我该如何着手解决这个问题呢?

有一个具体的原因说明为什么你所做的不应该起作用。如果您编写的代码按照您编写的方式工作,那么您将对空对象调用callSomeMethod。正如本杰明·林德利(Benjamin Lindley)所说,正确的方法是将代码放入try块中。这样,变量在作用域中,但方法调用只有在没有引发异常的错误alloc的情况下才会发生。

有一个特定的原因说明您所做的操作不起作用。如果您编写的代码按照您编写的方式工作,那么您将对空对象调用callSomeMethod。正如本杰明·林德利(Benjamin Lindley)所说,正确的方法是将代码放入try块中。这样,变量在作用域中,但方法调用只有在没有引发异常的错误alloc时才会发生。

对于后代,如果您需要在try块中执行其他操作,则不会导致无效指针,因为如前所述,您尝试执行的操作不起作用是有原因的。如果您正在尝试执行其他操作,但之后仍希望对某些操作执行操作,则以下代码将执行您想要的操作:

#include <iostream>

#include <memory>
#include <utility>

using namespace std;

int main()
{
    class SomeClass { public: int a; void callSomeMethod() const {} };

    std::unique_ptr<SomeClass> something{};

    try {
        something = std::move(std::make_unique<SomeClass>());

        // operation that might throw other_exception
    }
    catch (const other_exception& e) {
        std::cout << "bad" << std::endl;
    }
    // ... lots of code I don't want to include in the try/catch-scope.
    something->callSomeMethod();

    return 0;
}

为了子孙后代,如果您需要在try块中执行其他操作,而这些操作不会导致无效指针,因为正如前面提到的,您尝试执行的操作不起作用是有原因的。如果您正在尝试执行其他操作,但之后仍希望对某些操作执行操作,则以下代码将执行您想要的操作:

#include <iostream>

#include <memory>
#include <utility>

using namespace std;

int main()
{
    class SomeClass { public: int a; void callSomeMethod() const {} };

    std::unique_ptr<SomeClass> something{};

    try {
        something = std::move(std::make_unique<SomeClass>());

        // operation that might throw other_exception
    }
    catch (const other_exception& e) {
        std::cout << "bad" << std::endl;
    }
    // ... lots of code I don't want to include in the try/catch-scope.
    something->callSomeMethod();

    return 0;
}

正如其他答案所提到的,您的代码通常应该在try/catch块中,因为如果捕获到异常,它就没有任何意义。我不知道你不想在try/catch中包含代码的原因是什么,因为你暗示了什么。callsomethod依赖于大量代码,而大量代码依赖于std::make_unique。如果大量代码不相关,则可以将std::make_唯一性延迟到大量代码之后

我认为有必要澄清一下,异常的目的是中止并处理异常情况。例外情况是,以下代码的执行可能会受到影响。因此,任何可能受到影响的代码,或者依赖于它的可传递代码,都应该包含在try/catch块中。根据定义,这是最小范围,也是您应该使用的范围。不多不少

有时,处理异常的代码可以在抛出异常的函数之间共享,但通常最好还是缩小范围并为每个异常编写必要的特定处理代码

可能值得注意的是,对于std::bad_alloc几乎什么都做不到,所以它通常不值得关注。此外,除非您有理由这样做,否则通常应该通过引用捕获异常。所以,你的代码应该是这样的

try {

    auto something = std::make_unique<SomeClass>;    

    // ... lots of code I don't want to include in the try/catch-scope.

    something.callSomeMethod();
}

catch (std::exception& e) {

    // you dun goofed...
}

正如其他答案所提到的,您的代码通常应该在try/catch块中,因为如果捕获到异常,它就没有任何意义。我不知道你不想在try/catch中包含代码的原因是什么,因为你暗示了什么。callsomethod依赖于大量代码,而大量代码依赖于std::make_unique。如果大量代码不相关,则可以将std::make_唯一性延迟到大量代码之后

我认为有必要澄清一下,异常的目的是中止并处理异常情况。例外情况是,以下代码的执行可能会受到影响。因此,任何可能受到影响的代码,或者依赖于它的可传递代码,都应该包含在try/catch块中。根据定义,这是最小范围,也是您应该使用的范围。不多不少

有时,处理异常的代码可以在抛出异常的函数之间共享,但通常最好还是缩小范围并为每个异常编写必要的特定处理代码

可能值得注意的是,对于std::bad_alloc几乎什么都做不到,所以它通常不值得关注。此外,除非您有理由这样做,否则通常应该通过引用捕获异常。所以,你的代码应该是这样的

try {

    auto something = std::make_unique<SomeClass>;    

    // ... lots of code I don't want to include in the try/catch-scope.

    something.callSomeMethod();
}

catch (std::exception& e) {

    // you dun goofed...
}
如果你真的必须使用try/catch块之外的东西,而不是,你真的不能,但是如果你坚持。。。那么,让自己不可能犯错是个好主意

在null unique_ptr上调用方法肯定会导致灾难,而且unique_ptr的初始化需要不必要的内存分配

另一种方法是使用boost::optional:

#include <iostream>

#include <boost/optional.hpp>

using namespace std;

int main()
{
    class SomeClass { public: int a; void callSomeMethod() const {} };

    boost::optional<SomeClass> something{};

    try {
        something = SomeClass();

        // operation that might throw other_exception
    }
    catch (const std::exception& e) {
        std::cout << "bad" << std::endl;
    }
    // ... lots of code I don't want to include in the try/catch-scope.
    if (something) {
        something.get().callSomeMethod();
    }
    return 0;
}
如果你是真的 lly必须使用try/catch块之外的东西,不,你真的不能,但是如果你坚持。。。那么,让自己不可能犯错是个好主意

在null unique_ptr上调用方法肯定会导致灾难,而且unique_ptr的初始化需要不必要的内存分配

另一种方法是使用boost::optional:

#include <iostream>

#include <boost/optional.hpp>

using namespace std;

int main()
{
    class SomeClass { public: int a; void callSomeMethod() const {} };

    boost::optional<SomeClass> something{};

    try {
        something = SomeClass();

        // operation that might throw other_exception
    }
    catch (const std::exception& e) {
        std::cout << "bad" << std::endl;
    }
    // ... lots of code I don't want to include in the try/catch-scope.
    if (something) {
        something.get().callSomeMethod();
    }
    return 0;
}


为什么不把代码放在try范围内呢?这是解决这个问题的正确方法;在你的尝试之上,然后在你的尝试中:something=std::make_unique;嗯,由于代码可读性。。。。但我想我应该将…大量代码部分提取到一个方法中,然后在同一个try-catch部分中调用该方法。@BenjaminLindley try-body必须尽可能窄;否则,您将捕获不应该捕获的异常。@elyse:这显然是错误的,因为尽可能窄的区域根本就没有try块。try块显然需要足够宽,以包含在其中创建的对象的使用?这是解决这个问题的正确方法;在你的尝试之上,然后在你的尝试中:something=std::make_unique;嗯,由于代码可读性。。。。但我想我应该将…大量代码部分提取到一个方法中,然后在同一个try-catch部分中调用该方法。@BenjaminLindley try-body必须尽可能窄;否则,您将捕获不应该捕获的异常。@elyse:这显然是错误的,因为尽可能窄的区域根本就没有try块。try块显然需要足够宽,以包含在其中创建的对象的使用。是的,我认为我目前的代码设计特别糟糕,因为我正要在catch子句中执行“return”,因此,永远不要认为以后对“死”对象调用callSomeMethod是一种愚蠢的设计。在try主体中添加更多的代码会捕获更多可能不需要的异常,也会掩盖您感兴趣的表达式的异常-1@elyse,你能解释一下你的观点吗?如果您将代码放在try块中,但存在来自bad alloc的异常,那么代码将按其应该的方式抛出和捕获。如果没有问题,那么它可以调用该方法。如果您担心该方法将抛出,那么您需要另一个嵌套的try或更好的设计。确实,你不想在一个try块中放入大量可能抛出的代码,但这不是手头上的问题。重新设计了代码,现在所有内容都在try部分,并对大部分代码进行了重构,使try块更小,更具可读性。是的,我认为我目前的代码设计特别糟糕,因为我准备在catch子句中执行一个“return”,所以我从来没有考虑过以后对一个“dead”对象调用callsomethod是一个愚蠢的设计。在try主体中添加更多的代码会捕获更多的异常,这可能不是我想要的,也会掩盖您感兴趣的表达式的异常-1@elyse,你能解释一下你的观点吗?如果您将代码放在try块中,但存在来自bad alloc的异常,那么代码将按其应该的方式抛出和捕获。如果没有问题,那么它可以调用该方法。如果您担心该方法将抛出,那么您需要另一个嵌套的try或更好的设计。确实,您不想在一个try块中放入大量可能抛出的代码,但这不是手头上的问题。重新设计了代码,现在所有内容都在try部分,并对大部分代码进行了重构,使try块更小,更具可读性。如果您要这样做,最好从catch子句中的函数返回。否则,您将对空指针调用callSomeMethod。是的,这是可能的,但我认为我将重构代码,因为如果mem alloc失败,没有特别的理由对“某物”进行任何工作。@BenjaminLindley我试图澄清一下我在编辑中的意思。这不是一个大的使用场景,但我想展示一下,如果出现了它,你会怎么做。可怜的是,在调用周围放一个if语句,它可以是空的@理查德霍奇斯,除非我忘记了四个小时前的事情,而不是在这个例子中,它真的只是一个例子。如果make_unique沿着行的某个地方抛出bad_alloc,那么异常将不会被捕获,并将在堆栈中冒泡。如果要这样做,最好从catch子句中的函数返回。否则,您将对空指针调用callSomeMethod。是的,这是可能的,但我认为我将重构代码,因为如果mem alloc失败,没有特别的理由对“某物”进行任何工作。@BenjaminLindley我试图澄清一下我在编辑中的意思。这不是一个大的使用场景,但我想展示一下,如果出现了它,你会怎么做。可怜的是,在调用周围放一个if语句,它可以是空的@理查德霍奇斯,除非我是
从四个小时前开始做一些事情,不在这个例子中,它实际上只是一个例子。如果make_unique在某个地方抛出bad_alloc,异常将不会被捕获,并会在堆栈中冒泡。我在某个地方读到,您应该分配一小段内存,可以在catch std::bad_alloc-语句中释放,这样您就有了一些内存,可以用来通知用户这个和那个,这是一个好主意还是坏主意?通常在不分配内存的情况下完成,例如std::cout、std::cerr、退出状态、未捕获异常等。。。。但最好的办法是永远不要耗尽内存,只要合理使用内存,这几乎是不可能的。虽然只是供将来参考,但通常最好不要切换答案。这是一个带有gui的应用程序,因此std:cout等不是很有用。关于“改变答案”这一点,我本应该给你的答案投上一票,而不是“回答”,我猜我按错了按钮,我现在就把它改回去。这可能是有争议的。用户知道应用程序崩溃了,他们需要知道原因吗?如果他们需要知道原因,系统日志是否足够好?操作系统会捕获OOM并终止应用程序吗?操作系统是否会报告OOM事件,等等。这可能是一个有用的技巧,但我不认为有一个明确的答案。我在某个地方读到过,你应该分配一点点内存,可以在catch std::bad_alloc-语句中释放,这样你就有了一些内存,可以用来通知用户这个和那个,这是一个好主意还是坏主意?通常在不分配内存的情况下完成,例如std::cout、std::cerr、退出状态、未捕获异常等。。。。但最好的办法是永远不要耗尽内存,只要合理使用内存,这几乎是不可能的。虽然只是供将来参考,但通常最好不要切换答案。这是一个带有gui的应用程序,因此std:cout等不是很有用。关于“改变答案”这一点,我本应该给你的答案投上一票,而不是“回答”,我猜我按错了按钮,我现在就把它改回去。这可能是有争议的。用户知道应用程序崩溃了,他们需要知道原因吗?如果他们需要知道原因,系统日志是否足够好?操作系统会捕获OOM并终止应用程序吗?操作系统是否会报告OOM事件,等等。这可能是一个有用的技巧,但我不认为有一个明确的答案。