禁止创建临时对象

Nav*_*een 26 c++ mfc temporary

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

CSingleLock(&m_criticalSection, TRUE);
Run Code Online (Sandbox Code Playgroud)

请注意,它正在创建一个CSingleLock类的未命名对象,因此在此语句之后,临界区对象会立即解锁.这显然不是编码员想要的.此错误是由简单的输入错误引起的.我的问题是,有些我可以防止在编译时自己创建类的临时对象,即上面的代码类型应该生成编译器错误.一般来说,我认为每当一个类尝试进行某种资源获取时,就不应该允许该类的临时对象.有没有办法强制执行呢?

Dan*_*ker 14

编辑:正如j_random_hacker所说,可以强制用户声明一个命名对象以取出锁定.

然而,即使你的班级以某种方式禁止临时创建,用户也会犯类似的错误:

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

// do other stuff, assuming lock is held
Run Code Online (Sandbox Code Playgroud)

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

另一个可能的错误

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

 // do other stuff, don't call delete on c...
Run Code Online (Sandbox Code Playgroud)

这会让你问"有什么方法可以阻止我的班级用户在堆上分配"吗?答案是一样的.

在C++ 0x中,通过使用lambdas,将有另一种方法来完成所有这些.定义一个功能:

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

该函数捕获了CSingleLock的正确用法.现在让用户这样做:

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

这对用户来说更加困难.一开始语法看起来很奇怪,但是[&]后跟一个代码块意味着"定义一个不带args的函数,如果我按名称引用任何东西,它是外面的东西的名称(例如包含的局部变量)让我通过非const引用访问它,所以我可以修改它.)


j_r*_*ker 5

首先,Earwicker提出了一些好处 - 你不能阻止每一次意外滥用这种结构.

但对于您的具体情况,实际上可以避免这种情况.那是因为C++确实对临时对象做了一个(奇怪的)区分:自由函数不能对非临时对象进行非const引用. 因此,为了避免突然进入和不存在的锁定,只需将锁​​定代码移出CSingleLock构造函数并移动到自由函数中(您可以让朋友避免将内部暴露为方法):

class CSingleLock {
    friend void Lock(CSingleLock& lock) {
        // Perform the actual locking here.
    }
};
Run Code Online (Sandbox Code Playgroud)

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

使用:

CSingleLock myLock(&m_criticalSection, TRUE);
Lock(myLock);
Run Code Online (Sandbox Code Playgroud)

是的,写起来稍微笨拙.但是现在,如果你尝试,编译器会抱怨:

Lock(CSingleLock(&m_criticalSection, TRUE));   // Error! Caught at compile time.
Run Code Online (Sandbox Code Playgroud)

因为非const ref参数Lock()不能绑定到临时.

也许令人惊讶的是,类方法可以在临时工作 - 这就是为什么Lock()需要成为一个自由函数.如果将friend说明符和函数参数放在顶部代码段中以创建Lock()方法,那么编译器将很乐意允许您编写:

CSingleLock(&m_criticalSection, TRUE).Lock();  // Yikes!
Run Code Online (Sandbox Code Playgroud)

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