除了您不需要在特定情况下从构造函数中抛出这一事实之外,因为如果您的互斥体尚未初始化,则pthread_mutex_lock
实际上返回一个EINVAL并且您可以在调用lock
as后抛出std::mutex
:
void
lock()
{
int __e = __gthread_mutex_lock(&_M_mutex);
// EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
if (__e)
__throw_system_error(__e);
}
那么通常从构造函数中抛出对于构造过程中的获取错误是可以的,并且符合RAII(资源获取是初始化)编程范式。
在 RAII 上查看此示例
void write_to_file (const std::string & message) {
// mutex to protect file access (shared across threads)
static std::mutex mutex;
// lock mutex before accessing file
std::lock_guard<std::mutex> lock(mutex);
// try to open file
std::ofstream file("example.txt");
if (!file.is_open())
throw std::runtime_error("unable to open file");
// write message to file
file << message << std::endl;
// file will be closed 1st when leaving scope (regardless of exception)
// mutex will be unlocked 2nd (from lock destructor) when leaving
// scope (regardless of exception)
}
关注这些陈述:
static std::mutex mutex
std::lock_guard<std::mutex> lock(mutex);
std::ofstream file("example.txt");
第一个语句是 RAII 和noexcept
. 在 (2) 中,很明显 RAII 应用在lock_guard
并且它实际上可以throw
,而在 (3) 中ofstream
似乎不是 RAII ,因为必须通过调用is_open()
检查failbit
标志来检查对象状态。
乍一看,它似乎还没有决定它是什么标准方式,并且在第一种情况下std::mutex
不抛出初始化,*与 OP 实现相反*。在第二种情况下,它会抛出从 中抛出的任何东西std::mutex::lock
,而在第三种情况下,根本就没有投掷。
注意差异:
(1) 可以声明为静态,实际会声明为成员变量 (2) 永远不会实际声明为成员变量 (3) 预期声明为成员变量,底层资源可能并不总是可用。
所有这些形式都是RAII;要解决这个问题,必须分析RAII。
- 资源:你的对象
- 获取(分配):您正在创建的对象
- 初始化:您的对象处于其不变状态
这不需要您在构造时初始化和连接所有内容。例如,当您创建一个网络客户端对象时,您实际上不会在创建时将其连接到服务器,因为它是一个缓慢的操作,并且会失败。您将改为编写一个connect
函数来做到这一点。另一方面,您可以创建缓冲区或仅设置其状态。
因此,您的问题归结为定义您的初始状态。如果在您的情况下,您的初始状态是必须初始化互斥锁,那么您应该从构造函数中抛出。相比之下,不初始化然后(如在 中所做的那样)并在创建互斥锁std::mutex
时定义您的不变状态就可以了。无论如何,不变量不一定会受到其成员对象的状态的影响,因为对象在公共方法和之间或通过公共方法发生变异。mutex_
locked
unlocked
Mutex
Mutex::lock()
Mutex::unlock()
class Mutex {
private:
int e;
pthread_mutex_t mutex_;
public:
Mutex(): e(0) {
e = pthread_mutex_init(&mutex_);
}
void lock() {
e = pthread_mutex_lock(&mutex_);
if( e == EINVAL )
{
throw MutexInitException();
}
else (e ) {
throw MutexLockException();
}
}
// ... the rest of your class
};