С++ Объект удалён, но добраться можно, почему так?

class A 
    { 
    public: 
        int d = 4; 
    }; 
    
    int main( )
{
    A *a = new A(); 
    cout << a->d << endl; 
    A* p = &(*a); 
    delete a; 
    p->d = 5; 
    a->d=3;
    cout << a->d << endl;
    return 0;
}

Поясните пожалуйста этот код. По какой причине p->d работает даже после удаления a? То есть после delete если сразу вывести p->d, там будет мусорное значение.

Верно ли я понимаю, что p попросту указывает на некоторую область памяти, и компилятор(при создании указателя) "узнал", как обрабатывать выражения типа p->d, и дальше ему уже без разницы, есть ли там реально какой-то объект, к которому мы собираемся обращаться другим способом?

И дальше в коде p можно продолжать использовать как указатель на объект? или мы рискуем получить какие-то конфликты с памятью?

Что именно делает delete? Вопрос задан с целью разобраться, что именно происходит. Прилагаю ассемблерный код.

A::A() [base object constructor]:
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        mov     rax, QWORD PTR [rbp-8]
        mov     DWORD PTR [rax], 4
        nop
        pop     rbp
        ret
main:
        push    rbp
        mov     rbp, rsp
        push    rbx
        sub     rsp, 24
        mov     edi, 4
        call    operator new(unsigned long)
        mov     rbx, rax
        mov     DWORD PTR [rbx], 0
        mov     rdi, rbx
        call    A::A() [complete object constructor]
        mov     QWORD PTR [rbp-24], rbx
        mov     rax, QWORD PTR [rbp-24]
        mov     eax, DWORD PTR [rax]
        mov     esi, eax
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     esi, OFFSET FLAT:_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
        mov     rdi, rax
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))
        mov     rax, QWORD PTR [rbp-24]
        mov     QWORD PTR [rbp-32], rax
        mov     rax, QWORD PTR [rbp-24]
        test    rax, rax
        je      .L3
        mov     esi, 4
        mov     rdi, rax
        call    operator delete(void*, unsigned long)
.L3:
        mov     rax, QWORD PTR [rbp-32]
        mov     DWORD PTR [rax], 5
        mov     rax, QWORD PTR [rbp-24]
        mov     DWORD PTR [rax], 3
        mov     rax, QWORD PTR [rbp-24]
        mov     eax, DWORD PTR [rax]
        mov     esi, eax
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     esi, OFFSET FLAT:_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
        mov     rdi, rax
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))
        mov     eax, 0
        mov     rbx, QWORD PTR [rbp-8]
        leave
        ret
__static_initialization_and_destruction_0(int, int):
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     DWORD PTR [rbp-4], edi
        mov     DWORD PTR [rbp-8], esi
        cmp     DWORD PTR [rbp-4], 1
        jne     .L7
        cmp     DWORD PTR [rbp-8], 65535
        jne     .L7
        mov     edi, OFFSET FLAT:_ZStL8__ioinit
        call    std::ios_base::Init::Init() [complete object constructor]
        mov     edx, OFFSET FLAT:__dso_handle
        mov     esi, OFFSET FLAT:_ZStL8__ioinit
        mov     edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
        call    __cxa_atexit
.L7:
        nop
        leave
        ret
_GLOBAL__sub_I_main:
        push    rbp
        mov     rbp, rsp
        mov     esi, 65535
        mov     edi, 1
        call    __static_initialization_and_destruction_0(int, int)
        pop     rbp
        ret

Ответы (1 шт):

Автор решения: Bloody.cpp

Это работает только из-за того, что вам повезло. Судя по всему перед этим куском памяти был другой кусок, который принадлежал вашему процессу. Потому менеджер памяти ничего вам не сказал. Был бы перед этим куском памяти другой кусок памяти, который бы принадлежал другому процессу - вам бы не удалось ничего в него записать (Это можно сделать, но не так).

→ Ссылка