2015-06-01 23 views
9

Tôi đã làm việc trên một con trỏ chia sẻ (được gọi là Xử lý) thực hiện cho công cụ trò chơi của dự án sinh viên của tôi, và chúng tôi chạy vào một lỗi mà chúng tôi không thể giải thích. Đối với một số lý do, tại một thời điểm nhất định trong nhà máy của chúng tôi, có một con trỏ bên trong không hợp lệ được chuyển đến một nhà máy thông qua một tay cầm, điều này gây ra sự cố trong quá trình tải nguyên mẫu của chúng tôi. Chúng tôi đã gỡ lỗi thông qua quy trình này hàng giờ và đã phá vỡ bất kỳ câu lệnh phức tạp nào xuống các phiên bản đơn giản nhất để gỡ lỗi dễ dàng hơn. Cuối cùng tôi đã phân lập được vấn đề xuống trình tạo bản sao của lớp Xử lý. Tuy nhiên, nó vẫn có vẻ như có một số bước trung gian nơi con trỏ bên trong được giải phóng. Tôi đọc tất cả các bài viết tôi có thể tìm thấy về những gì có thể gây ra vấn đề này, và không tìm thấy bất cứ điều gì. Cuối cùng, tôi quyết định xem xét việc tháo gỡ và xem liệu tôi có thể tìm ra điều gì đang xảy ra không. Đây là hàm tạo bản sao mà không cần tháo gỡ.x86/C++ - Con trỏ tới con trỏ: Const bị vi phạm bởi trình biên dịch?

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

Đây là trình tạo bản sao có tháo gỡ.

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 
} 

Vấn đề (như xa như tôi có thể nói) là ở đây:

013FFF6D rep stos dword ptr es:[edi] 

Như mọi người đều biết, điều này được sử dụng để xóa bộ nhớ một cách nhanh chóng. Tuy nhiên, hướng dẫn này là xóa con trỏ mà con trỏ trỏ đến con trỏ trỏ đến, và không chỉ vậy, mà nó còn vi phạm const, như được truyền trong Handle là tham chiếu const. Tôi không chắc đó có phải là vấn đề VS hay không, một số loại cờ được thiết lập trong cài đặt xây dựng của chúng tôi khiến nó xảy ra, nhưng trình tạo bản sao hoạt động tốt cho đến khi chúng tôi cố tạo các nguyên mẫu với nó.

Bất kỳ trợ giúp nào sẽ được đánh giá rất nhiều!

EDIT 1: Tôi đã nghiên cứu thêm vấn đề sau khi một số nhận xét đưa ra ánh sáng mà tôi có thể trả về một biến tạm thời ở đâu đó trong mã của tôi. Tôi đã không thể tìm thấy bất cứ điều gì có thể đề nghị tôi đã làm như vậy, nhưng tôi đã sàng lọc thông qua một số tháo gỡ nhiều hơn để xem nếu tôi có thể tìm thấy bất kỳ gợi ý. Đây là chức năng CreateGenericArchetype của chúng tôi, đó là nơi mà vấn đề được cắt xén.

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

Dòng đó là gây ra tất cả các vấn đề của chúng tôi (tại thời điểm này) là thế này:

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

Đương nhiên, tôi mở rộng gỡ lỗi của tôi để CreateArchetypeComponent là tốt, vì vậy đây là quá:

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

Object được thừa hưởng từ Component, vì vậy yếu hèn static_cast của Object để Component là hợp lệ, và các nhà xây dựng xây dựng thành công Handle từ Component *. Vấn đề nảy sinh khi chúng ta cố gắng trả về Handle mà chúng ta đã xây dựng bên trong hàm.Đây là tháo gỡ của sự tương tác rằng:

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 

******* trở lại 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) 

******* constructor sao chép tháo từ ở trên đây *******

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

Trừ khi nhà xây dựng bản sao của tôi được gọi với "created_component" như xử lý rhs được coi là trả về biến cục bộ, tôi không thể thấy nó có thể xảy ra ở đâu , u nless đó là bởi vì các xử lý trả về từ CreateArchetypeComponent là trên stack khi nó được truyền cho constructor sao chép và sau đó được xóa.

+3

Có vẻ như lệnh 'rep' chỉ xóa hết ngăn xếp và không chạm vào đối tượng. –

+2

Được bình chọn cho đối tượng hiếm hoi đó: một câu hỏi tốt, được nghiên cứu kỹ lưỡng! –

+1

BTW, việc xóa con trỏ 'const' không vi phạm' const'. Bạn có thể làm tốt 'const int * const p = new int [42]; delete [] p; 'Và nếu bạn có một con trỏ trỏ tới một cá thể' const', thì con trỏ đầu tiên là 'const', nhưng không phải là dữ liệu trỏ tới (trong trường hợp này là con trỏ thứ hai), vì vậy bạn có thể" rõ ràng "con trỏ thứ hai. – vsoftco

Trả lời

0

Cảm ơn tất cả sự trợ giúp tuyệt vời với vấn đề này, nhưng chúng tôi đã tìm ra những gì đang diễn ra. Điều gì đã xảy ra là trong một số các hàm tạo xử lý, chúng ta cần phân bổ bộ nhớ cho con trỏ mà con trỏ trỏ tới con trỏ của Bộ xử lý sẽ trỏ đến. Vì vậy, những gì đã xảy ra là xử lý chính nó đã được sử dụng một con trỏ đã được tổ chức trên ngăn xếp, và khi opcode rep stos được gọi là, nó đã được thanh toán bù trừ cho biết con trỏ.

+0

Vì vậy, bạn đã có một con trỏ lơ lửng đến một biến tự động đã đi ra khỏi phạm vi? –

+0

Loại vật thể đang được chỉ định là mới trong nhà máy, nên nó nằm trên đống. Tuy nhiên, xử lý chính nó sử dụng một con trỏ đến con trỏ, và dữ liệu giữ địa chỉ mà các xử lý cần thiết để chỉ là trên stack. Khi 'rep stos' được thực hiện, nó xóa địa chỉ khỏi ngăn xếp. Vì vậy, con trỏ trỏ đến trỏ đến một vị trí trên ngăn xếp mà chính nó đã trỏ đến một vị trí trên heap. –

+0

Nhưng "vị trí trên ngăn xếp" (thực sự là một biến tự động của loại con trỏ) đã đi ra khỏi phạm vi, chính xác? –

Các vấn đề liên quan