构造函数中的C ++捕获异常

()

You've created a class and during its construction an exception is thrown. Question: should you catch it? Answer, it depends!

您已经创建了一个类,并且在其构造过程中引发了异常。 问题:你应该抓住它吗? 答案,这取决于!

Let's explore.

让我们来探索。

When deciding whether to catch the exception ask yourself a very simple question. If you do catch it can you still leave the class in a usable state? If the answer is yes, then by all means catch it, deal with it and allow construction to continue. If the answer is no then do not catch it! Why? Simple! if you catch and swallow it, how is the code that was attempting to construct your class to know that the class is unusable (or, at least only partially constructed)?

在决定是否捕获异常时,请问自己一个非常简单的问题。 如果您确实抓住了它,还可以让班级保持可用状态吗? 如果答案是肯定的,那么一定要抓住它,加以处理,然后继续进行建设。 如果答案是否定的,那就不要抓住它! 为什么? 简单! 如果捕获并吞下它,试图构造您的类的代码如何知道该类不可用(或至少仅部分构造)?

You could add a method that returns a boolean value to identify the class failed to construct but that will only lead to more complications and will just make life hard for everyone; you and the user of our class. You'll be giving them the abitilty to work with a partionally constructed object and the chances are that isn't a sane thing to do.

您可以添加一个方法,该方法返回一个布尔值以标识构造失败的类,但这只会导致更多的复杂性,并使每个人都过得很艰难。 您和我们班的用户。 您将赋予他们处理部分构造的对象的雄心,而这并不是一件理智的事情。

For example, what happens if the user tries to call a function on your partial construction, and almost certainly, not-so-sane class? What if failure to construct has left the class with an unassigned member pointer and that calling any or all of the member functions requires the pointer to be dereference it? Bad things will happen. It will end in tears. Kaboom!

例如,如果用户尝试在您的部分构造上调用函数,并且几乎可以肯定的是,它并不那么健全,该怎么办? 如果构造失败使类留下了未分配的成员指针,并且调用任何或所有成员函数要求将指针取消引用该怎么办? 坏事会发生。 它将以眼泪结束。 b!

Your solution? You'll now have to add even more code in each public function to check for this kind of issue to make sure the program doesn't crash. Every new function you add will need this check. You'll then have to decide how to deal with the function call if you can't honour it due to the class being in a insane state. A much more sensible option is to just throw an exception and let it emit from the class, giving the user of your class the option to catch it and deal with the case that construction failed.

您的解决方案? 现在,您必须在每个公共函数中添加更多代码以检查此类问题,以确保程序不会崩溃。 您添加的每个新功能都需要进行此检查。 然后,如果由于类处于疯狂状态而无法兑现它,则必须决定如何处理函数调用。 一个更明智的选择是只引发一个异常并让其从类中发出,从而为您的类用户提供了捕获该异常并处理构造失败的情况的选项。

Okay, so you decide to let the exception emit, but how does that help? It's pretty simply really when you think about it. The stack will unwind until it hits a catch handler. The original class you tried to construct is now out of scope. This is because try blocks and catch blocks are seperate scopes. At this point the object of your class cannot be used.

好的,所以您决定让异常发出,但是这有什么帮助呢? 当您考虑时,它确实非常简单。 堆栈将展开,直到到达catch处理程序为止。 您尝试构造的原始类现已超出范围。 这是因为try块和catch块是单独的作用域。 此时,您的类的对象无法使用。

By simply throwing and allowing an exception to emit from the constructor of your class you are making it clear to the end user that construction has failed and the class must not be used. In fact, better; because emiting the exception cases the stack to unwind all access to your class will now be out of scope, so the user can't attempt to use the partionally constructed object, even if they wanted to.

通过简单地抛出并允许从类的构造函数中发出异常,您可以使最终用户清楚构造失败,并且不得使用该类。 实际上更好; 因为发出异常情况,用于释放对类的所有访问权限的堆栈现在将超出范围,所以用户即使愿意也无法尝试使用部分构造的对象。

It should be noted that whilst you can catch and swallow an exception within the constructor of a class by creating a try/catch inside the function block the same is not true if you use a function try/catch block. This is a special type of try/catch where the actual function body itself is a try/catch block. These are especially useful in constructor functions because, unlike try/catch blocks inside the constructor function body, these can catch exceptions that are thrown by class members whilst they are being initialised in the classes constructor list.

应该注意的是,尽管您可以通过在功能块内创建try / catch来捕获和吞下类的构造函数中的异常,但如果使用功能try / catch块,则情况并非如此。 这是一种特殊的try / catch类型,其中实际的函数主体本身是try / catch块。 这些在构造函数中特别有用,因为与构造函数内部的try / catch块不同,它们可以捕获类成员在类构造函数列表中初始化时抛出的异常。

In the case of this type of try/catch, even if you don't explicitly re-throw a caught exception the compiler will do it for you. In other words, the compiler is telling you that you really shouldn't be writing constructors that can fail silently. Constructors should be as light as possible and do the bare minimum to create a sane object. Any additional set-up should be the remit of an "initialise" function.

对于这种类型的try / catch,即使您没有明确地重新引发捕获的异常,编译器也会为您完成。 换句话说,编译器告诉您,您实际上不应该编写可能会静默失败的构造函数。 构造函数应尽可能轻巧,并尽可能地减少以创建合理的对象。 任何其他设置都应该是“初始化”功能的一部分。

The difference between the constructor and the initialise function is that the constructor's job is just to create a class instance that isn't completely insane. For example, consider a bank account class. The constructor might initialise the balance to zero (not just let it take on some arbitrary value), whereas, the initialise function would then take arguments to actually set the balance of the account for the specific context of use.

构造函数和初始化函数之间的区别在于,构造函数的工作只是创建一个并非完全疯狂的类实例。 例如,考虑一个银行帐户类别。 构造函数可能会将余额初始化为零(不仅仅是让它取一些任意值),而初始化函数随后将使用参数来针对特定的使用上下文实际设置帐户的余额。

This is a trivial example and probably not the best argument for using an initialise function, but it makes the point; constructors are just to create a sane object whilst initialisation functions are to then set up that sane object for actual use.

这是一个简单的示例,可能不是使用初始化函数的最佳参数,但这很重要。 构造函数只是创建一个合理的对象,而初始化函数则是为了实际使用而设置该合理的对象。

So far so good, but now here's another question for you: if a class constructor emits an exception what happens when its destructor gets executed? For example, let's say it throws half way through and there are still a number of pointers that are to have memory allocated to them that the destructor is responsible for releasing. How does the destructor know what to do?

到目前为止,一切都很好,但是现在这又是一个问题:如果类构造函数发出异常,则在执行其析构函数时会发生什么? 例如,假设它抛出了一半,仍然有许多指针要分配给析构函数负责释放的内存。 析构函数如何知道该怎么办?

Here's the kicker - it doesn't because there is no way it could! More importantly, because it doesn't know it never even bothers trying. What? Yes, it's true, if a class constructor throws and emits an exception the class destructor is never called. Think about it, how can the compiler destruct a class that was never actually constructed? It just wouldn't make any sense.

这是踢球者-不是因为没有办法! 更重要的是,因为它不知道它甚至不会费力地尝试。 什么? 是的,的确如此,如果类构造函数抛出并发出异常,则永远不会调用类析构函数。 考虑一下,编译器如何破坏从未真正构造的类? 只是没有任何意义。

Of course, this leads to a bit of a dilemma. What happens to the resources that were allocated before the exception was thrown? What releases them? Answer: nothing does! Or, more specifically, nothing can because the destructor never gets executed and by the time you know the class has failed to construct it has already gone out of scope. Basically, you have yourself a big fat resource leak. Life sucks, eh?

当然,这导致了一些难题。 引发异常之前分配的资源会怎样? 是什么释放它们? 答:什么都不做! 或者,更具体地说,什么也不能做,因为析构函数永远不会执行,并且到您知道类构造失败时,它已经超出范围。 基本上,您有大量的脂肪资源泄漏。 生活糟透了,是吗?

The solution is to use the RAII design pattern. It's a fancy acronym (that stands for "Resource Acquisition Is Initialisation") that basically means use smart pointers (or, at least use management classes for your resources). Although the destructor on the class that emits the exception is not called, any members that have been constructed up until that point do have their own destructors called. This means that as long as your class members are smart pointer type objects that are able to clean up after themselves upon destruction they will take care of cleaning up the partially constructed class for you.

解决方案是使用RAII设计模式 。 这是一个花哨的首字母缩写(代表“资源获取就是初始化”),基本上意味着使用智能指针(或至少对资源使用管理类)。 尽管没有调用发出异常的类上的析构函数,但是直到此刻为止构造的任何成员都将调用自己的析构函数。 这意味着,只要您的类成员是智能指针类型的对象,它们就可以在销毁后自行清除,它们将为您清除部分构造的类。

See, life isn't so bad after all. Time for a nice cup of tea, I think!

瞧,生活毕竟还算不错。 我想是时候喝杯好茶了!

Further reading:

进一步阅读:

翻译自: https://www.experts-exchange.com/articles/18351/C-Catching-exceptions-in-constructors.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值