2015-06-01 15 views
9

Stavo lavorando a un puntatore condiviso (chiamato Handle) per il motore di gioco del mio progetto per studenti, e ci siamo imbattuti in un bug che non siamo riusciti a spiegare. Per qualche ragione, a un certo punto del nostro stabilimento, c'era un puntatore interno non valido passato a una fabbrica attraverso un handle, che causava un arresto anomalo durante il caricamento del nostro archetipo. Abbiamo eseguito il debug di tutto il processo per ore e abbiamo eliminato qualsiasi istruzione complessa fino alle loro versioni più semplici per semplificare il debug. Alla fine ho isolato il problema fino al costruttore di copia della classe Handle. Tuttavia, sembrava ancora che ci fosse un passaggio intermedio in cui il puntatore interno veniva liberato. Ho letto ogni articolo che ho trovato su cosa potrebbe causare questo problema e non ho trovato nulla. Alla fine, ho deciso di guardare allo smontaggio e vedere se riuscivo a capire cosa stava succedendo. Ecco il costruttore di copie senza lo smontaggio.x86/C++ - Puntatore al puntatore: Const viene violato dal compilatore?

template <class TYPE> 
Handle<TYPE>::Handle(const Handle<TYPE>& rhs, bool weak) : m_weak(weak), m_ref_count(rhs.m_ref_count), m_ptr(rhs.m_ptr) 
{ 
    if(!m_weak) 
    ++(*m_ref_count); 
} 

Questo è il costruttore di copie con lo smontaggio.

template <class TYPE> 
Handle<TYPE>::Handle(const Handle<TYPE>& rhs, bool weak) : m_weak(weak), m_ref_count(rhs.m_ref_count), m_ptr(rhs.m_ptr) 
{ 
013FFF50 push   ebp 
013FFF51 mov   ebp,esp 
013FFF53 sub   esp,0CCh 
013FFF59 push   ebx 
013FFF5A push   esi 
013FFF5B push  edi 
013FFF5C push  ecx 
013FFF5D lea   edi,[ebp-0CCh] 
013FFF63 mov   ecx,33h 
013FFF68 mov   eax,0CCCCCCCCh 
013FFF6D rep stos dword ptr es:[edi] 
013FFF6F pop   ecx 
013FFF70 mov   dword ptr [this],ecx 
013FFF73 mov   eax,dword ptr [this] 
013FFF76 mov   cl,byte ptr [weak] 
013FFF79 mov   byte ptr [eax],cl 
013FFF7B mov   eax,dword ptr [this] 
013FFF7E mov   ecx,dword ptr [rhs] 
013FFF81 mov   edx,dword ptr [ecx+4] 
013FFF84 mov   dword ptr [eax+4],edx 
013FFF87 mov   eax,dword ptr [this] 
013FFF8A mov   ecx,dword ptr [rhs] 
013FFF8D mov   edx,dword ptr [ecx+8] 
013FFF90 mov   dword ptr [eax+8],edx 
    if(!m_weak) 
013FFF93 mov   eax,dword ptr [this] 
013FFF96 movzx  ecx,byte ptr [eax] 
013FFF99 test  ecx,ecx 
013FFF9B jne   Handle<Component>::Handle<Component>+60h (013FFFB0h) 
    ++(*m_ref_count); 
013FFF9D mov   eax,dword ptr [this] 
013FFFA0 mov   ecx,dword ptr [eax+4] 
013FFFA3 mov   edx,dword ptr [ecx] 
013FFFA5 add   edx,1 
013FFFA8 mov   eax,dword ptr [this] 
013FFFAB mov   ecx,dword ptr [eax+4] 
013FFFAE mov   dword ptr [ecx],edx 
} 

Il problema (per quanto posso dire) è qui:

013FFF6D rep stos dword ptr es:[edi] 

Come tutti sanno, questo è usato per cancellare la memoria rapidamente. Tuttavia, questa istruzione sta cancellando il puntatore a cui punta il puntatore interno del puntatore rhs, e non solo quello, ma sta violando const, dato che il passato in Handle è un riferimento const. Non sono sicuro che si tratti di un problema di VS, una sorta di flag impostato nelle impostazioni di build che sta causando il verificarsi, ma il costruttore di copia funziona bene finché non proviamo a creare archetipi con esso.

Qualsiasi aiuto sarebbe molto apprezzato!

EDIT 1: Ho ulteriormente analizzato il problema dopo che alcuni dei commenti hanno evidenziato che potrei restituire una variabile temporanea da qualche parte nel mio codice. Non sono stato in grado di trovare nulla che suggerisse che stavo facendo così, ma ho setacciato un po 'più di smontaggio per vedere se potevo trovare qualche suggerimento. Ecco la nostra funzione CreateGenericArchetype, che rappresenta il punto in cui il problema si sta verificando.

Handle<Object> ObjectFactory::CreateGenericArchetype(const std::wstring &ArchetypeName) 
{ 
    /* Create The Object */ 
    auto archetype = mComponentFactoryMap.find(std::wstring(L"Object")); 
    Handle<Component> created_component = archetype->second->CreateArchetypeComponent(); 
    Handle<Object> retVal = static_handle_cast<Object>(created_component); 
    retVal->mArchetypeName = ArchetypeName; 
    mArchetypeMap.insert(std::pair<std::wstring, Handle<Object>>(ArchetypeName, retVal)); 

    return Handle<Object>(retVal, true); 
} 

La linea che sta causando tutti i nostri problemi (al momento) è questo:

Handle<Component> created_component = archetype->second->CreateArchetypeComponent(); 

Naturalmente, ho esteso il mio debug per CreateArchetypeComponent così, ecco che anche:

Handle<Component> ComponentTypeFactory<Object>::CreateArchetypeComponent() const 
{ 
    Object* created_object = new Object; 
    Component* casted_object = static_cast<Component*>(created_object); 
    Handle<Component> newComponent(casted_object); 
    newComponent->mType = mType; 
    newComponent->mID = GetNewID(); 
    return newComponent; 
} 

L'oggetto eredita da Component, quindi il downcast statico_cast dell'Oggetto a Componente è valido e il costruttore costruisce correttamente l'Handle dal Componente *. Il problema si verifica quando proviamo a restituire la maniglia che abbiamo costruito all'interno della funzione.Ecco lo smontaggio di tale interazione:

return newComponent; 
005602AD push  0 
005602AF lea   eax,[newComponent] 
005602B2 push  eax 
005602B3 mov   ecx,dword ptr [ebp+8] 
005602B6 call  Handle<Component>::Handle<Component> (04EA050h) 
005602BB mov   ecx,dword ptr [ebp-110h] 
005602C1 or   ecx,1 
005602C4 mov   dword ptr [ebp-110h],ecx 
005602CA mov   byte ptr [ebp-4],0 
005602CE lea   ecx,[newComponent] 
005602D1 call  Handle<Component>::~Handle<Component> (04F2E7Bh) 
005602D6 mov   eax,dword ptr [ebp+8] 
} 
00EB02D9 push  edx 
00EB02DA mov   ecx,ebp 
00EB02DC push  eax 
00EB02DD lea   edx,ds:[0EB0318h] 
00EB02E3 call  @[email protected] (0E4BA9Eh) 
00EB02E8 pop   eax 
00EB02E9 pop   edx 
00EB02EA mov   ecx,dword ptr [ebp-0Ch] 
00EB02ED mov   dword ptr fs:[0],ecx 
00EB02F4 pop   ecx 
00EB02F5 pop   edi 
00EB02F6 pop   esi 
00EB02F7 pop   ebx 
00EB02F8 mov   ecx,dword ptr [ebp-10h] 
00EB02FB xor   ecx,ebp 
00EB02FD call  @[email protected] (0E4E9BAh) 
00EB0302 add   esp,130h 
00EB0308 cmp   ebp,esp 
00EB030A call  __RTC_CheckEsp (0E41C3Dh) 
00EB030F mov   esp,ebp 
00EB0311 pop   ebp 
00EB0312 ret   4 

******* ******* torna a CreateGenericArchetype

005C2639 call  __RTC_CheckEsp (04F1C3Dh) 
005C263E mov   dword ptr [ebp-1D4h],eax 
005C2644 mov   ecx,dword ptr [ebp-1D4h] 
005C264A mov   dword ptr [ebp-1D8h],ecx 
005C2650 mov   byte ptr [ebp-4],4 
005C2654 mov   edx,dword ptr [ebp-1D8h] 
005C265A push  edx 
005C265B lea   ecx,[created_component] 
005C265E call  Handle<Component>::Handle<Component> (04EA050h) 

costruttore di copia ******* smontaggio da sopra qui *******

005C2663 mov   byte ptr [ebp-4],6 
005C2667 lea   ecx,[ebp-174h] 
005C266D call  Handle<Component>::~Handle<Component> (04F2E7Bh) 
    Handle<Object> retVal = static_handle_cast<Object>(created_component); 

a meno che il mio costruttore di copia viene chiamato con il "created_component", come il RHS maniglia è considerato il ritorno una variabile locale, non posso vedere dove potrebbe essere andato storto , u nless è perché l'handle restituito da CreateArchetypeComponent è nello stack quando viene passato al costruttore di copie e viene quindi cancellato.

+3

Sembra che l'istruzione 'rep' stia semplicemente eliminando lo stack e non tocchi alcun oggetto. –

+2

Upvoted per quell'oggetto raro: una domanda decente, ben ricercata! –

+1

BTW, l'eliminazione di un puntatore 'const' non sta violando' const'. Si può benissimo fare 'const int * const p = new int [42]; delete [] p; 'E se hai un puntatore al puntatore in un'istanza' const', il primo puntatore è 'const', ma non i dati a cui punta (in questo caso il secondo puntatore), quindi puoi" cancella "il secondo puntatore. – vsoftco

risposta

0

Grazie per il grande aiuto con questo problema, ma abbiamo capito cosa stava succedendo. Quello che stava succedendo è che in alcuni costruttori di Handle avevamo bisogno di allocare memoria per il puntatore a cui puntava il puntatore interno al puntatore. Quindi, ciò che stava accadendo era che il Handle stesso stava usando un puntatore che era tenuto in pila, e quando fu chiamato l'opcode rep stos, stava cancellando il puntatore.

+0

Quindi hai puntato un puntatore a una variabile automatica che stava andando fuori campo? –

+0

Tipo di ... l'oggetto puntato era nuovo nella fabbrica, quindi era sul mucchio. Tuttavia, la stessa Handle utilizza un puntatore al puntatore e i dati contenenti l'indirizzo che l'handle necessario per indicare era nello stack. Quando viene eseguito 'rep stos', cancella l'indirizzo dallo stack. Quindi il puntatore al puntatore stava puntando a una posizione sullo stack che stava puntando a una posizione sull'heap. –

+0

Ma la "posizione sullo stack" (in realtà una variabile automatica del tipo di puntatore) era già uscita dal campo di applicazione, corretto? –

Problemi correlati