在“破解编码面试”一书中单例中的错误/问题?

时间:2010-12-12 00:21:21

标签: c++

在破解编码访谈一书的第259页,给出了C ++中模板化的单例(我不想发布所有代码,以防它的副本正确)。

问题是将单例实现为模板,并假设一个名为Lock的类,确保其线程安全且异常安全。

对于使用双重锁定技术且Lock对象具有acquire()/ release()对的单例,你可能会得到答案。

但是这个课没有析构函数。这是一个错误吗?如果它有一个析构函数,因为类实例成员是静态的,析构函数只会在程序终止时被调用,如果程序终止,任何分配的内存都会被释放回系统。或者是吗?是否存在不会发生这种情况的情况,因此没有析构函数的单身人士会导致泄密?

其次,问题是说使单例异常安全。使用未捕获的新对象创建单例对象,并且Lock对象是静态的,因此这实际上是异常安全吗?如果没有用于创建单例的内存,则new抛出异常,但由于Lock对象是静态的,因此无法调用其release()方法,因此它永远不会被调用?

3 个答案:

答案 0 :(得分:1)

首先,我要提醒的是,辛格尔顿被广泛认为是一种反模式 - 这似乎是一个好主意,但事实证明这是一个错误。

其次,很难确定没有看到代码,但我的直接猜测是,如果你想让它异常安全,你可能最好将它们提供的锁定对象包装在一个释放锁定的RAII包装器中它的dtor:

class real_lock {
    Lock lock;
public:
    real_lock() { lock.acquire(); }
    ~real_lock() { lock.release(); }
};

有了这个,异常安全(至少是Lock部分)非常简单。 OTOH,双重检查锁定也是一种反模式。几乎任何你使用它的东西都至少会在某些机器/某些情况下出现问题。

答案 1 :(得分:1)

异常安全并不意味着捕获异常。这意味着使用RAII和自动析构函数来确保在出现异常时正确进行清理。

对于互斥锁,正确的方法是使用静态互斥对象和自动范围保护样式获取/释放RAII对象。由于RAII对象具有自动存储功能,因此会在发生异常时释放互斥锁。

编辑:这是RAII类的正确形式

class scoped_lock_guard
{
    Lock& m_lock;
public:
    scoped_locK_guard(Lock& lock) : m_lock(lock) { lock.acquire(); }
    ~scoped_lock_guard() { m_lock.release(); }
};

Lock对象本身必须以某种方式共享。

答案 2 :(得分:-1)

在本书的开头部分,作者建议她将用Java编写“几乎所有”解决方案(第42页)。

Java GC将负责静态实例的清理。