在调试多线程应用程序中的崩溃时,我终于在以下语句中找到了问题:

CSingleLock(&m_criticalSection, TRUE);

请注意,它正在创建 CSingleLock 类的未命名对象,因此临界区对象在此语句之后立即解锁。这显然不是程序员想要的。此错误是由一个简单的打字错误引起的。我的问题是,是否可以通过某种方式阻止在编译时创建类的临时对象,即上述类型的代码应该会生成编译器错误。一般来说,我认为每当一个类尝试进行某种资源获取时,就不应该允许该类的临时对象。有什么办法可以强制执行吗?

有帮助吗?

解决方案

修改:如图j_random_hacker笔记,可以强制用户为了取出一个锁来声明命名对象

然而,即使临时工的创作在某种程度上禁止类,那么用户可以做一个类似的错误:

// take out a lock:
if (m_multiThreaded)
{
    CSingleLock c(&m_criticalSection, TRUE);
}

// do other stuff, assuming lock is held

最终,用户必须了解的代码行,他们写的影响。在这种情况下,他们必须知道他们正在创建一个对象,他们必须知道会持续多久。

另一个可能的错误:

 CSingleLock *c = new CSingleLock(&m_criticalSection, TRUE);

 // do other stuff, don't call delete on c...

这将导致你问“有什么办法,我可以从堆中分配它停我的课的用户”?这个问题的答案将是相同的。

在的C ++ 0x会有另一种方式做到这一切,通过使用lambda表达式。定义一个函数:

template <class TLock, class TLockedOperation>
void WithLock(TLock *lock, const TLockedOperation &op)
{
    CSingleLock c(lock, TRUE);
    op();
}

这功能捕获CSingleLock的正确用法。现在,让用户做到这一点:

WithLock(&m_criticalSection, 
[&] {
        // do stuff, lock is held in this context.
    });

这是更难为用户弄糟。语法看起来奇怪在第一,但[b],然后一个代码块的意思是“定义一个函数,无参数,如果我指的是什么名字,它是东西之外的名称(例如,在包含一个局部变量功能)让我通过非const引用访问它,所以可对其进行修改。)

其他提示

第一的, Earwicker 提出了一些很好的观点 ——你无法阻止这种结构的每一次意外误用。

但对于你的具体情况,这实际上是可以避免的。这是因为 C++ 对于临时对象确实做出了一个(奇怪的)区别: 自由函数不能采用对临时对象的非常量引用。 因此,为了避免锁突然出现或消失,只需将锁​​定代码移出 CSingleLock 构造函数并转换为自由函数(您可以将其作为朋友来避免将内部暴露为方法):

class CSingleLock {
    friend void Lock(CSingleLock& lock) {
        // Perform the actual locking here.
    }
};

解锁仍然在析构函数中执行。

使用方法:

CSingleLock myLock(&m_criticalSection, TRUE);
Lock(myLock);

是的,写起来有点麻烦。但现在,如果您尝试以下操作,编译器会抱怨:

Lock(CSingleLock(&m_criticalSection, TRUE));   // Error! Caught at compile time.

因为非常量 ref 参数 Lock() 无法绑定到临时对象。

也许令人惊讶的是,类方法 对临时对象进行操作——这就是为什么 Lock() 需要是一个自由函数。如果您删除 friend 顶部代码片段中的说明符和函数参数 Lock() 一个方法,那么编译器会很高兴地允许你写:

CSingleLock(&m_criticalSection, TRUE).Lock();  // Yikes!

MS 编译器注意事项: Visual Studio .NET 2003 之前的 MSVC++ 版本错误地允许函数绑定到 VC++ 2005 之前版本中的非常量引用。 此行为已在 VC++ 2005 及更高版本中修复.

没有,没有这样做的方式。这样做会破坏几乎是严重依赖于创建无名的临时所有的C ++代码。您的特定类唯一的解决办法就是让它们的构造私人,然后总是通过一些工厂建造它们。但我认为治愈是比疾病更糟糕!

我不这么认为。

虽然这不是一个明智的做法 - 因为你已经和你的bug发现 - 没有什么“非法”的有关语句。编译器无法知道从该方法的返回值是否为“重要的”或不的方式。

编译器不应该禁止临时对象创建,IMHO。

特殊情况下,像缩小向量你真的需要临时对象被创建。

std::vector<T>(v).swap(v);

虽然这是有点困难,但仍代码审查和单元测试应该抓住这些问题。

另外,这里是一个穷人的解决方案:

CSingleLock aLock(&m_criticalSection); //Don't use the second parameter whose default is FALSE

aLock.Lock();  //an explicit lock should take care of your problem

有关下列哪些?稍微滥用职权预处理器,但它是足够聪明的,我认为它应该包括:

class CSingleLock
{
    ...
};
#define CSingleLock class CSingleLock

现在遗忘命名一个错误的临时结果,因为虽然以下是有效的C ++:

class CSingleLock lock(&m_criticalSection, true); // Compiles just fine!

在相同的代码,但省略了名称,是不是:

class CSingleLock(&m_criticalSection, true); // <-- ERROR!

我看到,在5年内没有人拿出了最简单的解决方案:

#define LOCK(x) CSingleLock lock(&x, TRUE);
...
void f() {
   LOCK(m_criticalSection);

现在只能用这个宏创建锁。没有机会创造的临时更多!这具有宏可以很容易地扩充以执行任何种类的在调试检查的构建,例如检测不适当的递归锁定的附加益处,记录文件和锁定的线,以及更多。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top