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.
Sembra che l'istruzione 'rep' stia semplicemente eliminando lo stack e non tocchi alcun oggetto. –
Upvoted per quell'oggetto raro: una domanda decente, ben ricercata! –
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