草庐IT

c++ - C++ 构造函数中的异常消除

coder 2023-06-04 原文

我们最近遇到了将 C++ 框架移植到运行 uClinux 的 ARM 平台的问题,该平台唯一支持供应商的编译器是 GCC 2.95.3。我们遇到的问题是异常非常不可靠,导致从根本没有被捕获到被不相关的线程(!)捕获。这似乎是一个记录在案的错误,即 herehere .

经过深思熟虑,我们决定完全消除异常,因为我们已经达到了异常对正在运行的应用程序造成很大损害的地步。现在主要关心的是如何管理构造函数失败的情况。

我们已经尝试过lazy evaluation ,其中每个方法都能够实例化动态资源并返回一个状态值,但这意味着每个类方法都必须返回一个返回值,这会在代码中产生 lot 的 ifs 并且非常烦人在通常不会导致错误的方法中。

我们考虑添加一个静态 create 方法,该方法返回一个指向已创建对象的指针,如果创建失败则返回 NULL,但这意味着我们不能再将对象存储在堆栈上,仍然需要通过如果您想对实际错误采取行动,请在对状态值的引用中。

根据 Google 的 C++ 风格指南,他们 do not use exceptions并且只在它们的构造函数中做琐碎的工作,使用 init 方法进行非平凡的工作( Doing Work in Constructors )。但是,我找不到任何关于他们在使用这种方法时如何处理构造错误的信息。

这里有没有人尝试过消除异常并想出一个很好的解决方案来处理构造失败?

最佳答案

对于堆栈上的对象,通常你会得到这样的代码:

MyClassWithNoThrowConstructor foo;
if (foo.init(bar, baz, etc) != 0) {
    // error-handling code
} else {
    // phew, we got away with it. Now for the next object...
}

这适用于堆上的对象。我假设您使用返回 NULL 而不是抛出的东西覆盖全局运算符 new,以节省您记住在任何地方都使用 nothrow new:

MyClassWithNoThrowConstructor *foo = new MyClassWithNoThrowConstructor();
if (foo == NULL) {
    // out of memory handling code
} else if (foo->init(bar, baz, etc) != 0) {
    delete foo;
    // error-handling code
} else {
    // success, we can use foo
}

显然,如果可能的话,使用智能指针来省去记住删除的麻烦,但是如果你的编译器不能正确地支持异常,那么你在获取 Boost 或 TR1 时可能会遇到麻烦。我不知道。

您还可能希望以不同的方式构造逻辑,或抽象组合的 new 和 init,以避免在处理多个对象时出现深度嵌套的“箭头代码”,并在两者之间共享错误处理失败案例。以上只是最苦心形式的基本逻辑。

在这两种情况下,构造函数都将所有内容设置为默认值(它可以接受一些参数,前提是它对这些参数的处理不可能失败,例如,如果它只是存储它们)。然后 init 方法可以完成真正的工作,这可能会失败,在这种情况下返回 0 成功或任何其他失败值。

您可能需要强制整个代码库中的每个 init 方法都以相同的方式报告错误:您确实想要一些返回 0 成功或负错误代码,一些返回 0 成功或正确的错误代码,一些返回 bool,一些返回具有解释错误字段的值的对象,一些设置全局 errno 等。

您或许可以快速查看一些在线 Symbian 类 API 文档。 Symbian 无一异常(exception)地使用 C++:它确实有一种称为“离开”的机制,可以部分弥补这一点,但从构造函数中离开是无效的,因此在设计非失败构造函数和推迟失败方面存在相同的基本问题初始化例程的操作。当然,对于 Symbian,init 例程是允许离开的,因此调用者不需要我上面指出的错误处理代码,但在 C++ 构造函数和额外的 init 调用之间的拆分工作方面,它是相同的。

一般原则包括:

  • 如果您的构造函数想要以可能失败的方式从某处获取值,请将其推迟到 init 并在 ctor 中保留默认初始化的值。
  • 如果您的对象包含一个指针,请在 ctor 中将其设置为 null 并在 init 中“正确”设置。
  • 如果你的对象持有一个引用,要么将其更改为(智能)指针,以便它可以以 null 开头,要么让调用者将值作为参数传递给构造函数,而不是在 ctor 中生成它。
  • 如果你的构造函数有对象类型的成员,那么你很好。他们的 ctor 也不会抛出任何异常,因此以通常的方式在初始化列表中构造您的成员(和基类)是完全可以的。
  • 确保跟踪设置的内容和未设置的内容,以便在初始化失败时析构函数起作用。
  • 除了构造函数、析构函数和 init 之外的所有函数都可以假定 init 已成功,前提是您为您的类提供文件证明在 init 被调用并成功之前调用除 init 之外的任何方法都是无效的。
  • 您可以提供多个 init 函数,与构造函数不同,它们可以相互调用,就像为某些类提供多个构造函数一样。
  • 您不能提供可能失败的隐式转换,因此如果您的代码当前依赖于引发异常的隐式转换,那么您必须重新设计。大多数运算符重载也是如此,因为它们的返回类型受到限制。

关于c++ - C++ 构造函数中的异常消除,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/334439/

有关c++ - C++ 构造函数中的异常消除的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  3. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  4. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  5. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  6. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  7. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  8. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

    刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

  9. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  10. ruby - rspec 需要 .rspec 文件中的 spec_helper - 2

    我注意到像bundler这样的项目在每个specfile中执行requirespec_helper我还注意到rspec使用选项--require,它允许您在引导rspec时要求一个文件。您还可以将其添加到.rspec文件中,因此只要您运行不带参数的rspec就会添加它。使用上述方法有什么缺点可以解释为什么像bundler这样的项目选择在每个规范文件中都需要spec_helper吗? 最佳答案 我不在Bundler上工作,所以我不能直接谈论他们的做法。并非所有项目都checkin.rspec文件。原因是这个文件,通常按照当前的惯例,只

随机推荐