경험 총결: 구조적 데이터 정렬 방식이 맵에 대한find의 영향

1 개발 환경
VC++ 2010
2 현상 묘사
2.1 구조 정의
/**          */
    typedefstruct tagNODE_QUERY_CONDITION
    {
        tagNODE_QUERY_CONDITION(DWORD nodeType,DWORD nodeId,bool includeChild, DWORDchildDepth, bool includeData)
        {
            this->nodeType= nodeType;
            this->nodeId = nodeId;
            this->includeChild= includeChild;
            this->childDepth= childDepth;
            this->includeData= includeData;
        }
 
        booloperator < (tagNODE_QUERY_CONDITION const& condition) const
        {
            returnmemcmp(this, &condition,sizeof(condition)) < 0;
        }
 
        booloperator == (tagNODE_QUERY_CONDITION const& condition) const
        {
            returnmemcmp(this, &condition,sizeof(condition))== 0;
        }
 
        DWORD nodeType;
        DWORD nodeId;
        boolincludeChild;
        DWORD childDepth;
        boolincludeData;      
    }NODE_QUERY_CONDITION;

 
2.2 사용 시 문제점
클래스에서 다음과 같은 구성원 변수를 정의했지만 다음 코드에서 auto it = m 발견queryBuffer.find(condition);,같은 인덱스는 Debug 버전에서 대응하는 item을 찾을 수 있으며,release 모드에서는 찾을 수 없습니다.
typedefstd::map CQueryBuffer;
    CQueryBuffer m_queryBuffer;

 
 
그리고 다음과 같은 방법에서 사용한다
void CPecNodeManagerDataQuerier::QueryData()
{
    CSingleLock lockQueue(&m_csQueue);
    if(!lockQueue.Lock())
        return;
 
    if(m_queryQueue.empty())
        return;
 
    CSingleLocklockBuffer(&m_csQueryBuffer);
    if(!lockBuffer.Lock())
        return;
 
    while(!m_queryQueue.empty())
    {
        tagNODE_QUERY_CONDITION condition =m_queryQueue.front();
        m_queryQueue.pop_front();
        CSetupNodeList nodeList;
        if(ReadSetupNodesByNodeTypeID(condition.nodeType,condition.nodeId, condition.includeChild, condition.childDepth, condition.includeData,nodeList))
        {
            auto it = m_queryBuffer.find(condition);
            if(it!= m_queryBuffer.end())
            {
                ASSERT(FALSE);
                CSetupNodeList& nodeList =it->second;
                CPublicFuncs::destroy_range(nodeList.begin(),nodeList.end());
                nodeList.clear();
            }
 
            m_queryBuffer[condition] = nodeList;
        }
    }
}

3 원인 분석
우선 vc 컴파일러에 문제가 있는 거 아닐까요?그러나 곧 이런 가능성을 배제할 것이다. 마이크로소프트가 이렇게 저급한 실수를 범할 수는 없다.
그리고 debug버전과release버전이 어떤 차이가 있는지 고려해야 한다. 가장 뚜렷한 차이점은 debug버전은 분배된 메모리를 초기화하고release버전은 그렇지 않다는 것이다.
이 예와 결합하면 맵의find를 사용했기 때문에find의 실현은tagNODE에 의존한다QUERY_CONDITION 의 = = 및
그리고 또 tagNODE를 보러...QUERY_CONDITION 구조의 이 두 연산자의 실현은 코드가 다음과 같다. 이런 비교적 게으른 방법은 어떻게 보면 좀 이상하다고 생각하지만 이해할 수 있다.
        booloperator < (tagNODE_QUERY_CONDITION const& condition) const
        {
            returnmemcmp(this, &condition,sizeof(condition)) < 0;
        }
 
        booloperator == (tagNODE_QUERY_CONDITION const& condition) const
        {
            returnmemcmp(this, &condition,sizeof(condition))== 0;
        }

그리고sizeof를 의심하기 시작했어요. 이게 되돌아오는 수치가 얼마예요?그리고 다시 보면 이 구조의 구성원 중 두 개의 bool이 있는데 32비트 또는 64비트 시스템에서 이 구조가 촘촘한 저장소를 사용하지 않으면 이 구조 대상이 차지하는 메모리가 실제 필요한 메모리보다 많다. 예를 들어 이 구조에 대해sizeof는 16으로 되돌아간다. 비록 실제로는 14만 필요하지만.
 
4 해결 방법
위의 분석에 따라 구조 정의를 수정하고 구조 앞뒤에 추가합니다.
#pragma pack(1)
…..
#pragma pack()
다시 컴파일한 후,release에서 대응하는item을 찾을 수 있습니다.
 
한층 더 분석하면return memcmp(this, &condition,sizeof(condition)<0;이런 실현 방식은 사실 결함이 있다. 예를 들어 나중에 구조를 바꾸는 데 더블 형식의 데이터 구성원을 추가하면 여기에서 되돌아오는 구조는 우리가 원하는 것이 아닐 수도 있다.
그래서 이 비교 방법을 정규적인 방식에 따라 실현하는 것을 건의한다. 예를 들어 다음과 같다.
  
  bool operator < (tagNODE_QUERY_CONDITION const& condition)const
        {
            If(this.nodeType < condition.nodeType)
                returntrue;
            else if(this.nodeId < condition.nodeId)
                returntrue;
            else if(this.includeChild< condition.includeChild)
                returntrue;
            elseif(this.childDepth < condition.childDepth)
                returntrue;
            else if(this.includeData < condition.includeData)
                returntrue;
            returnfalse;
        }

좋은 웹페이지 즐겨찾기