C++ by default creates objects by value (opposed to any other language) and when the variable goes out of scope the variable is cleaned up.
'new' you use when you want to make a global raw pointer outside of the normal memory system is how I would see it. You really never use it normally at least I don't.
A good rule of thumb is not to use 'new'.
Why? Because the blog post is titled "Understanding C++ Ownership System".
In production, odds are you are relying on allocators or containers others already wrote. You coming in in 2026 may not ever use the keywords directly, but you'll either be using abstractions that handle that for you (be it STL or something internal) or using some custom allocation call referring to memory already allocated.
But yes, I'd say a more general rule is "allocate with caution".
If it's done as part of a "here is legacy code, suggest ways to improve it" question one should point it out, though.
A codebase can use only std::make_unique() to allocate heap, and still pass around raw pointers to that memory (std::unique_ptr::get()).
The real problem is data model relying on manual lifetime synchronization, e.g. pass raw pointer to my unique_ptr to another thread, because this thread joins that thread before existing and killing the unique_ptr.
That rule of thumb is only a useful rule if you don't care about how memory works and are comfortable with abstractions like RAII. That's fine for lots of real code but dismissing `new` and `delete` on principle is not interesting or productive for any discussion.
Anyway the article is quite approachable, do not take my criticism to shy away from writing!
The modern, safest, way to use C++, is to use smart pointers rather than raw pointers, which guarantee that nothing gets deleted until there are no more references to it, and that at that point it will get deleted.
Of course raw pointers and new/delete, even malloc/free, all have their uses, and without these low level facilities you wouldn't be able to create better alternatives like smart pointers, but use these at your own peril, and don't blame the language if you mess up, when you could have just done it the safe way!
To be more precise, C++'s smart pointers will ensure something is live while specific kinds of references the smart pointer knows about are around, but they won't (and can't) catch all references. For example, std::unique_ptr ensures that no other std::unique_ptr will own its object and std::shared_ptr will not delete its object while there are other std::shared_ptrs around that point to the same object, but neither can track things like `std::span`/`std::string_view`/other kinds of references into their object.
C++'s non-owning "view" classes are a different matter, and the issue there isn't ownership but lifetime of the view vs the underlying data the view is referencing (which in case of string_view could be literally anywhere - a local array of char, a malloc'd block of memory, etc!!).
I'm not a fan of a lot of the (relatively) more recent additions to C++. I think C++14 was about the peak! Given that C++ is meant to be usable as a systems programming language, not just for applications, and given that many new features being added to C++ are really library additions, not language ones, then it's important for the language to include unsafe lower level facilities than can be used for things like that, but actually encouraging application developers to use classes like this that are error-prone by design seems questionable!
Sure, I was just clarifying that "references" in the bit I quoted covers specific things and not everything with reference-like behavior.
As soon as you start mix 'n' matching smart pointers and raw pointers into what they are pointing to, then all bets are off!
Sure, and if you get into it there are two issues here.
1) Smart pointers are a leaky abstraction that still gives you access to the underlying raw pointer if you want, with which you can shoot yourself in the foot.
2) It's not the smart pointer's fault, but the type you use them to point to might also be a leaky abstraction (e.g. std::string, which again allows access to the underlying raw pointer), or might just be some hot garbage the developer themselves wrote!
Python 2 to 3 is a good example of what can be expected to happen - very slow adoption of the new version since companies may just not have the resources or desire to rewrite existing code that is running without problems.
Epochs was given the usual WG21 treatment and that's the end of that. Rust shipped 2021 Edition, and 2024 Edition, and I see no reason to think 2027 Edition won't happen.
The current iteration of Bjarne's "Profiles" idea is in a similar ballpark though it got there via a very different route. This time because it will aid safety to outlaw things that are now considered a bad idea. If this goes anywhere its nearest ship vehicle is C++ 29.
Now, Python 2 to Python 3 did take a few years, maybe a decade. But just shipping the mechanism to reform C++ looks likely to take at least nine years. Not the reform, just the mechanism to enable it.
int x = 123;
delete &x;
and that would compile. But it's not a very good idea and you should be able to, well, not do that.In modern C++, we avoid allocating and deallocating ourselves, as much as possible. But of course, if you jump to arbitrary code, or overwrite something that's due as input for deallocation with the wrong address, or similar shenanigans, then - it could happen.
It is a 'char *buffer' type, unless I'm mistaken raw pointers don't have methods/member functions?
Edit: clarity
[1] https://btmc.substack.com/p/memory-unsafety-is-an-attitude-p...
[2] https://www.gingerbill.org/series/memory-allocation-strategi...
[3] https://dmitrysoshnikov.com/compilers/writing-a-pool-allocat...
But even in software using these strategies, they probably will be using different ownership strategies in different parts of the code. Once you're writing high performance code, you will use specific strategies that give you the best results. But it's completey domain specific.
It's not a matter of one being strictly better than the other, but rather about using the right tool for the job.
How are you conceptualizing this that the arena allocator is simpler?
How are you conceptualizing smart pointers as "obscuring" ownership, when the entire point of them is to make ownership explicit! The smart pointer IS the owner!
Title: “ Casey Muratori | Smart-Pointers, RAII, ZII? Becoming an N+2 programmer”
The best argument I've ever come across against using RAII is that you end up with these nests of objects pointing to one another, and if something fails, the cleanup code can really only do one thing, which is unwind and deallocate (or whatever the cleanup path is). This structure, generally, precludes the possibility of context dependent resource re-usage on initialization failure, or on deallocation, because you kind of have to have only one deallocation path. Obviously, you could imagine supporting in an RAII context, but, the point is that you probably have to put a fair bit of conscious effort into doing that, whereas if you have a less .. rigid.. ownership model, it becomes completely trivial.
I agree that the allocation model and ownership model are independent concepts. I mentioned arena allocation because the people I know that reject the traditional C++ ownership model generally tend to favor arenas, scratch space, freelists, etc. I'm specifically interested in an ownership model that works with arenas, and tracks ownership of the group of allocations, as opposed to the typical case we think about with RAII where we track ownership of individual allocations.
If an exception gets thrown, causing your RAII object scope to be exited, then no problem - the object destructor gets called and the resource gets released (this is the entire point of RAII - to make resource allocation and deallocation automatic and bullet-proof).
If you are creating spaghetti-like cross-referencing data structures, then that is either poor design or something you are doing deliberately because you need it. In either case, it has nothing to do with RAII, and RAII will work as normal and release resources when the managing object is destroyed (e.g. by going out of scope).
RAII could obviously be used to allocate/free a resource (e.g. temp buffer) from a free list, but is not really relevant to arena allocation unless you are talking about managing the allocation/release of the entire arena. The whole point of an arena allocator is the exact opposite of RAII - you are deliberately disconnecting individual item allocation from release so that you instead do a more efficient bulk (entire arena) release.
Unlike the GP suggests, and like you suggest, I have indeed embraced RAII in the library - generally, not just w.r.t. memory allocation. I have not, however, replicated that idioms of the standard library. So, for example:
* My allocations are never typed.
* The allocation 'primitives' return a memory_region type - essentially a pointer and a size; I discourage the user from manipulating raw pointers.
* Instead of unique_ptr's, I encourage the use of unique_span's: owning, typed, lightweight-ish containers - like a fusion of std::span<T> and std::unique_ptr<T[]> .
I wonder if that might seem less annoying to GP.
---
These aren't mutually exclusive; you can use the former to manage the latter, after all.
> I know there are groups of highly productive programmers that feel the traditional C++ ownership model is hot garbage
I'm not aware of links off the top of my head, but I can try to summarize the argument.
From my understanding, the argument against RAII/etc. has more to do with the mindset it supposedly encourages more than the concept itself - that RAII and friends makes it easy to think more in terms of individual objects/elements/etc. instead of batches/groups, and as a result programmers tend to follow the easy path which results in less performant/more complex code. By not providing such a feature, so the argument goes, programmers no longer have access to a feature which makes less-efficient programming patterns easy and so batched/grouped management of resources becomes more visible as an alternative.
https://youtu.be/TGfQu0bQTKc?si=7TiDRic6LaWI1Xpc&t=70
"In Rust you need to worry about borrowing. In C++ you don't have to worry about borrowing; in C++ you have to worry about ownership, which is an old concept..." :-P