如何在不使用异常的情况下检查构造函数()中的失败?

Cod*_*Lab 4 c++ constructor exception

我正在处理的所有类都有Create()/ Destroy()(或Initialize()/ Finalized())方法.

创建()方法返回的值是布尔像的下方.

bool MyClass::Create(...);
Run Code Online (Sandbox Code Playgroud)

所以我可以从返回值检查实例的初始化是否成功.

没有Create()/ Destroy()我可以在constructor()和析构函数()中做同样的工作,但我无法解决下面的问题.

谁能帮我?提前致谢.

我不能使用例外,因为我的公司不喜欢它.

class Foo
{
private:
    AnotherClass a;
public:
    Foo()
    {
        if(a.Initialize() == false)
        {
            //???
            //Can I notify the failure to the user of this class without using exception?
        }
    }
    ...
};

Foo obj;
Run Code Online (Sandbox Code Playgroud)

R S*_*hko 8

如果您不想使用异常,有两种方法可以让调用者知道构造函数是否成功:

  1. 构造函数接受一个参数/指针,该参数将错误状态传递给调用者.
  2. 该类实现了一个返回构造函数的错误状态的方法.调用者将负责检查此方法.

如果您使用这些技术中的任何一种,请确保您的析构函数可以处理构造函数失败的实例.


Mik*_*our 7

无一例外,C++ 本质上是一种与 C++ 完全不同的语言,其中许多赋予 C++ 独特表达能力的习惯用法都变得无能为力。正如您所指出的,构造函数被剥夺了它们的用处,并且所有重要的初始化都必须移至可以返回错误指示的第二阶段伪构造函数中。(有些人还出于误导性的对称性而主张使用匹配的伪析构函数,但这完全没有意义)。或者,构造函数可以在成功时设置一个“构造”标志,并且每个类都可以有一个“构造”方法来检查这个类及其所有子类。

如果您的公司要求您禁用异常,那么您还需要一个公司范围(或至少项目范围)的约定来替换它。您需要为要返回的所有(重要)函数定义一个类型,并在任何地方一致地使用该类型 - 否则您将得到一个不可维护的布尔值大杂烩和在每个级别传递和手动转换的不兼容枚举。

在这种情况下,Foo还需要一个Initialize方法,该方法可以a.Initialize在失败时调用并退出。


Jor*_*lon 5

我也遇到过这个问题。我认为一个优雅的解决方案是将真正的构造函数设为私有,然后使用工厂返回实例和错误。

我不热衷于通过输出参数检索错误,因此我将成功和可能的实例放在一个结构中:

template < class T >
struct expect {
   expect( T v ) :
      failure(false),
      value(v) // or std::move(v)
   {
   }

   expect( int e ) :
      failure(true),
      error(e)
   {
   }

   bool failure;
   union {
      T value;
      int error;
   };
};

class A {
   public:
      expect<A> create( /* arguments */ ) {
         if( /* check fails */ ) {
            return expect(error_code);
         }
         return expect( A(/*arguments*/) );
      }
   private:
      A( /* constructor that does not fail */ );
};
Run Code Online (Sandbox Code Playgroud)

这是一种流行的模式,被提议作为标准的扩展。优点是您的代码仍然可以使用 RAII。

我建议观看 Andrei Alexandrescu关于 C++ 中系统错误处理的演讲

编辑:现在是 2023 年,这现在是 C++23 标准的一部分!请参阅: https: //en.cppreference.com/w/cpp/utility/expected

这是 UNIX 的打开/关闭接口的示例(我不确定该std::error_code部分是否正确)。

class A {
public:
    using A_or_error = std::expected<A, std::error_code>;

    static A_or_error create(const char *path) {
        int fd = open(path, O_RDONLY);
        if (fd == -1)
            return A_or_error(std::unexpect, errno, std::system_category());
        return A_or_error(A(fd));
    }
    
    ~A() { close(fd); }

private:
    A(int fd) : fd(fd) {}

    int fd;
};
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/z/7xnG6jY7e