문제

멀티 스레드 애플리케이션에서 충돌을 디버깅하는 동안 마침내이 진술에서 문제를 찾았습니다.

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에는 Lambdas를 사용 하여이 모든 것을 수행하는 또 다른 방법이 있습니다. 함수 정의 :

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.
    });

이것은 사용자가 망치기가 훨씬 어렵습니다. 구문은 처음에는 이상하게 보이지만 코드 블록이 뒤 따르는 코드 블록은 "Args를 취하지 않는 함수를 정의하고 이름으로 아무것도 언급하는 경우 외부의 이름입니다 (예 : 포함의 로컬 변수입니다. 기능) 비정상적인 참조로 액세스 할 수 있도록 수정할 수 있습니다.)

다른 팁

첫 번째, 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.

비 초가의 참조 매개 변수 때문입니다 Lock() 임시에 묶을 수 없습니다.

아마도 놀랍게도 수업 방법 ~할 수 있다 임시에서 운영하십시오. 그래서 그렇기 때문에 그 이유입니다 Lock() 자유 기능이어야합니다. 당신이 떨어 뜨리면 friend 상단 스 니펫의 지정자 및 기능 매개 변수 Lock() 방법은 컴파일러가 행복하게 쓸 수 있도록합니다.

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

MS 컴파일러 참고 : MSVC ++ Visual Studio .NET 2003까지의 MSVC ++ 버전은 VC ++ 2005 이전의 버전에서 비정규 참조에 결합 할 수있는 기능을 잘못 허용했습니다. 이 동작은 VC ++ 2005 이상에서 수정되었습니다..

아니요,이 작업을 수행 할 방법이 없습니다. 그렇게하면 이름이없는 시간을 만드는 데 크게 의존하는 거의 모든 C ++ 코드를 깨뜨릴 수 있습니다. 특정 클래스를위한 유일한 솔루션은 생성자를 비공개로 만들고 항상 일종의 공장을 통해 구성하는 것입니다. 그러나 나는 치료가 질병보다 나쁘다고 생각합니다!

나는 그렇게 생각하지 않습니다.

당신이 당신의 버그로 알게 된 것처럼, 그것은 현명한 일이 아니지만, 진술에 대해 "불법적"은 없습니다. 컴파일러는 메소드의 리턴 값이 "중요"인지 아닌지 알 수있는 방법이 없습니다.

컴파일러는 임시 객체 생성, 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