Two places
A local variable lives on the stack and disappears when its function returns. The heap is a big pool of memory you ask for while the program runs. What you take stays yours until you give it back.
Asking with new
new int(42) asks the heap for room for one int, puts 42 in it and returns its address, here 0x5a10. The int has no name. The pointer p, on the stack, keeps that address so you can find it.
*p means: go to the address stored in p. Line 4 reads the int there (42), adds 1 and writes 43 back. p itself does not change; only the int on the heap does.
new int[3]{1, 2, 3} reserves three ints side by side and fills them. It returns the first one's address, 0x5a30, and a keeps it. The size could come from a variable known only while running.
Through the pointers, heap ints work like any other. *p is 43 and a[2] is 3, so line 6 prints 46.
Giving it back
delete p returns the int to the heap, which can hand that memory out again. p still holds the old address, but nothing valid lives there now: p is a dangling pointer. Never use *p after delete.
Memory from new[] goes back with delete[], with the brackets. Mixing them up, delete for an array or delete[] for one int, is undefined behaviour. Now the heap holds nothing of ours.
At the closing brace main returns, and its stack frame goes away with p and a. The heap was cleaned up first, by our two deletes. Every new had its delete.
Without lines 7 and 8, the pointers still vanish at the brace, but the ints stay reserved with no way left to reach them. That is a memory leak. std::unique_ptr and std::vector call delete for you.
What happens
;; step 1/9 · two-places
stack local variables of the running functions
made and removed automatically, in order
heap (the standard says: free store)
you ask: new → it gives memory
you return: delete → it takes it back
lives as long as you decide, not one function call
The addresses 0x5a10 and 0x5a30 are made up and short so they fit. Real ones are longer (for example 0x55d4c3a1b2c0 on 64-bit Linux), change from run to run, and blocks usually have gaps and bookkeeping bytes between them. An int is 4 bytes on the usual desktop platforms (64-bit Linux, macOS and Windows), which is why a[1] sits 4 bytes after a[0]. The standard only promises at least 16 bits.