It should be noted that it is, in the case of C++, a common misconception that "you need to do manual memory management". In fact, you don't usually do any memory management in your code.

Fixed-size objects (with scope lifetime)

In the vast majority of cases when you need an object, the object will have a defined lifetime in your program and is created on the stack. This works for all built-in primitive data types, but also for instances of classes and structs:

class MyObject { public: int x; }; int objTest() { MyObject obj; obj.x = 5; return obj.x; }

Stack objects are automatically removed when the function ends. In Java, objects are always created on the heap, and therefore have to be removed by some mechanism like garbage collection. This is a non-issue for stack objects.

Objects that manage dynamic data (with scope lifetime)

Using space on the stack works for objects of a fixed size. When you need a variable amount of space, such as an array, another approach is used: The list is encapsuled in a fixed-size object which manages the dynamic memory for you. This works because objects can have a special cleanup function, the destructor. It is guaranteed to be called when the object goes out of scope and does the opposite of the constructor:

class MyList { public: // a fixed-size pointer to the actual memory. int* listOfInts; // constructor: get memory MyList(size_t numElements) { listOfInts = new int[numElements]; } // destructor: free memory ~MyList() { delete[] listOfInts; } }; int listTest() { MyList list(1024); list.listOfInts[200] = 5; return list.listOfInts[200]; // When MyList goes off stack here, its destructor is called and frees the memory. }

There is no memory management at all in the code where the memory is used. The only thing we need to make sure is that the object we wrote has a suitable destructor. No matter how we leave the scope of listTest , be it via an exception or simply by returning from it, the destructor ~MyList() will be called and we don't need to manage any memory.

(I think it is a funny design decision to use the binary NOT operator, ~ , to indicate the destructor. When used on numbers, it inverts the bits; in analogy, here it indicates that what the constructor did is inverted.)

Basically all C++ objects which need dynamic memory use this encapsulation. It has been called RAII ("resource acquisition is initialization"), which is quite a weird way to express the simple idea that objects care about their own contents; what they acquire is theirs to clean up.

Polymorphic objects and lifetime beyond scope

Now, both of these cases were for memory which has a clearly defined lifetime: The lifetime is the same as the scope. If we do not want an object to expire when we leave the scope, there is a third mechanism which can manage memory for us: a smart pointer. Smart pointers are also used when you have instances of objects whose type varies at runtime, but which have a common interface or base class:

class MyDerivedObject : public MyObject { public: int y; }; std::unique_ptr<MyObject> createObject() { // actually creates an object of a derived class, // but the user doesn't need to know this. return std::make_unique<MyDerivedObject>(); } int dynamicObjTest() { std::unique_ptr<MyObject> obj = createObject(); obj->x = 5; return obj->x; // At scope end, the unique_ptr automatically removes the object it contains, // calling its destructor if it has one. }

There is another kind of smart pointer, std::shared_ptr , for sharing objects among several clients. They only delete their contained object when the last client goes out of scope, so they can be used in situations where it is completely unknown how many clients there will be and how long they will use the object.

In summary, we see that you don't really do any manual memory management. Everything is encapsulated and is then taken care of by means of completely automatical, scope-based memory management. In the cases where this is not enough, smart pointers are used which encapsulate raw memory.

It is considered extremely bad practice to use raw pointers as resource owners anywhere in C++ code, raw allocations outside of constructors, and raw delete calls outside of destructors, as they are almost impossible to manage when exceptions occur, and generally hard to use safely.

The best: this works for all types of resources

One of the biggest benefits of RAII is that it's not limited to memory. It actually provides a very natural way to manage resources such as files and sockets (opening/closing) and synchronization mechanisms such as mutexes (locking/unlocking). Basically, every resource that can be acquired and must be released is managed in exactly the same way in C++, and none of this management is left to the user. It is all encapsulated in classes which acquire in the constructor and release in the destructor.

For example, a function locking a mutex is usually written like this in C++:

void criticalSection() { std::scoped_lock lock(myMutex); // scoped_lock locks the mutex doSynchronizedStuff(); } // myMutex is released here automatically

Other languages make this much more complicated, by either requiring you to do this manually (e.g. in a finally clause) or they spawn specialized mechanisms which solve this problem, but not in a particularly elegant way (usually later in their life, when enough people have suffered from the shortcoming). Such mechanisms are try-with-resources in Java and the using statement in C#, both of which are approximations of C++'s RAII.

So, to sum it up, all of this was a very superficial account of RAII in C++, but I hope that it helps readers to understand that memory and even resource management in C++ is not usually "manual", but actually mostly automatic.