我正在与一位同事讨论从构造函数抛出异常的问题,我想我需要一些反馈。

从设计的角度来看,从构造函数抛出异常是否正确?

假设我在一个类中包装了一个POSIX互斥锁,它看起来像这样:

class Mutex {
public:
  Mutex() {
    if (pthread_mutex_init(&mutex_, 0) != 0) {
      throw MutexInitException();
    }
  }

  ~Mutex() {
    pthread_mutex_destroy(&mutex_);
  }

  void lock() {
    if (pthread_mutex_lock(&mutex_) != 0) {
      throw MutexLockException();
    }
  }

  void unlock() {
    if (pthread_mutex_unlock(&mutex_) != 0) {
      throw MutexUnlockException();
    }
  }

private:
  pthread_mutex_t mutex_;
};

我的问题是,这是标准的方法吗?因为如果pthread mutex_init调用失败,互斥锁对象将不可用,因此抛出异常将确保不会创建互斥锁。

我是否应该为Mutex类创建一个成员函数init,并调用pthread mutex_init,其中将返回基于pthread mutex_init的返回的bool值?这样我就不必为如此低级的对象使用异常了。


当前回答

唯一不从构造函数抛出异常的情况是,如果您的项目有禁止使用异常的规则(例如,谷歌不喜欢异常)。在这种情况下,你不希望在构造函数中使用异常,而必须使用某种类型的init方法。

其他回答

如果确实从构造函数抛出异常,请记住,如果需要在构造函数初始化列表中捕获异常,则需要使用函数try/catch语法。

e.g.

func::func() : foo()
{
    try {...}
    catch (...) // will NOT catch exceptions thrown from foo constructor
    { ... }
}

vs.

func::func()
    try : foo() {...}
    catch (...) // will catch exceptions thrown from foo constructor
    { ... }

抛出异常是处理构造函数失败的最佳方式。您尤其应该避免只构造了一半对象,然后依靠类的用户通过测试某种类型的标志变量来检测构造失败。

在相关的一点上,您有几种不同的异常类型来处理互斥错误,这一点让我有点担心。继承是一个很好的工具,但它可能被过度使用。在这种情况下,我可能更喜欢一个muterror异常,可能包含一个信息丰富的错误消息。

虽然我没有在专业水平上使用过c++,但在我看来,从构造函数抛出异常是可以的。我在. net中这样做(如果需要的话)。看看这个和这个链接。你可能会感兴趣。

从构造函数抛出是可以的,但应该确保这一点 对象是在main启动之后和main启动之前构造的 完成:

class A
{
public:
  A () {
    throw int ();
  }
};

A a;     // Implementation defined behaviour if exception is thrown (15.3/13)

int main ()
{
  try
  {
    // Exception for 'a' not caught here.
  }
  catch (int)
  {
  }
}
#include <iostream>

class bar
{
public:
  bar()
  {
    std::cout << "bar() called" << std::endl;
  }

  ~bar()
  {
    std::cout << "~bar() called" << std::endl;

  }
};
class foo
{
public:
  foo()
    : b(new bar())
  {
    std::cout << "foo() called" << std::endl;
    throw "throw something";
  }

  ~foo()
  {
    delete b;
    std::cout << "~foo() called" << std::endl;
  }

private:
  bar *b;
};


int main(void)
{
  try {
    std::cout << "heap: new foo" << std::endl;
    foo *f = new foo();
  } catch (const char *e) {
    std::cout << "heap exception: " << e << std::endl;
  }

  try {
    std::cout << "stack: foo" << std::endl;
    foo f;
  } catch (const char *e) {
    std::cout << "stack exception: " << e << std::endl;
  }

  return 0;
}

输出:

heap: new foo
bar() called
foo() called
heap exception: throw something
stack: foo
bar() called
foo() called
stack exception: throw something

析构函数不被调用,因此如果需要在构造函数中抛出异常,很多东西(例如。打扫?)要做的事。