避免使用 "throwing" 构造函数进行堆分配

Avoiding heap allocation with "throwing" constructors

本文关键字:分配 构造函数 throwing      更新时间:2023-10-16

假设我有一些类Foo没有定义默认构造函数,throws在非默认构造函数中。在初始化该类型的新对象时,我希望捕获任何异常并返回,否则继续使用该对象。我注意到,如果可能的话,很难在堆栈或通过使用共享指针来初始化这个对象,因为我试图避免管理内存。

失败1

Foo f; // doesn't work, no default constructor
try { f = Foo(...); }

失败2

try {
  Foo f(...)
}
catch(...) {}
// doesn't work, f is inaccessible
失败

3

boost::shared_ptr<Foo> pf;
try { pf = new Foo(...); } // no assignment operator

我必须…

Foo *f;
try { f = new Foo(...) } // okay, let's just manage the memory

有办法吗?

编辑

好的,这是可行的,尽管不是最干净的。有没有更"标准"的方式?

boost::shared_ptr<Foo> pf;
try { pf = boost::shared_ptr<Foo>(new Foo(...)); }

智能指针有一个reset方法:

boost::shared_ptr<Foo> f;
//...
f.reset(new Foo(...));

这解决了你的"失败#3",让你可以做你想做的事情。

保持f堆栈为基础的正确方法是尊重范围:

try {
  Foo f(...);
  ... entire code using f ...
}
catch(...) {}

一个解决方案是使用boost::optional(或c++ 14的std::optional):

boost::optional<Foo> f;
try { f = Foo(...); }

如果您使用正确的.reset()方法,您的智能指针情况和喜欢工作很好。

然而,这个问题不适合正常的用例,要么是类设计不当,要么是您使用不当。正常使用应该像case#1一样,将F放在块内。或者不尝试阻塞并将catch留给上游。

编辑:针对最近的评论和原始问题,我坚持自己的立场,即在最终用户代码中使用try块是不受欢迎的。对于使用特定抛出策略处理第三方组件的情况,如果不符合我的需要,我会编写包装器,将异常转换为错误返回或错误代码抛出异常。并使用扩展组件

在这种情况下,用户被抛出tor所困扰。因此,它可以通过在函数中包装该actor来处理:

Foo* new_foo( ARGS )
{
    try{
       return new Foo( ARGS );
    }
    catch( const FooException& )
    {
         return NULL;
    }
}

然后让客户端代码没有try块,更重要的是,没有赋值。只要const unique_ptr<Foo> p(new_foo(...))就行了。