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

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

假设我在一个类中包装了一个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方法。

其他回答

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

是的,从失败的构造函数抛出异常是做到这一点的标准方式。有关更多信息,请阅读有关处理构造函数失败的FAQ。有init()方法也可以,但是每个创建互斥对象的人都必须记住init()必须被调用。我觉得这违背了RAII原则。

注意,从构造函数抛出异常后,析构函数永远不会被调用。

struct B
{
    char* p;
    B() { 
        cout << "Constructor - B" << endl; 
        p = new char[1024];
        throw std::exception("some exception");
    }
    ~B() { // NEVER GETS CALLED AFTER EXCEPTION !!!! - memory leak 
        cout << "Destructor - B" << endl; 
        delete[] p;
    } 
};

int main()
{
    try {
        B b;
    }
    catch (...) {
        cout << "Catch called " << endl;
    }
}

输出:

Constructor - B
Catch called       (Note: B's Destructor is NEVER called)
#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

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

如果确实从构造函数抛出异常,请记住,如果需要在构造函数初始化列表中捕获异常,则需要使用函数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
    { ... }