Unless there is significant reason to avoid garbage collection (for the vast majority of software there isn’t) then Crystal is the best C++ replacement out there. Its Ruby syntax is easy to read and write, its OOP model derives from Smalltalk (via Ruby). And it has easy interop with C++ code.
Unless you use shared_ptr for all heap allocations (and are also careful about references to stack-allocated data) it allows for uaf bugs. And at that point you are probably better off with a GC since you've got all of the problems with a reference-counting GC but none of the opportunities for optimization.
Modern C++ provides enough nuts and bolts to avoid explicit allocations. My current C++ project (backend business server) only has single one. The rest is handled happily by RAII (what a dumb name). I could eliminate that single explicit allocation as well but I think I could manage single occurrence ;).
Also It would be really dumb to use shared_ptr for every case of allocation
"I only every allocate things on the stack" is not sufficient to have safe lifetime access. I've seen enough cases of some stack-allocated string getting passed as a string_view and then blowing up because somebody stored it.
"I have a complete tree of object ownership with a single allocated root" is also a mess for a different reason - you literally never delete anything. Either you have a very very strange application or you are being massively wasteful with memory pressure.
It would be really dumb to use shared_ptr for every allocation. But it is the only way in C++ to systematically ensure that you never have a uaf (I guess you could also have a custom allocator and a custom pointer type that does a null check on every dereference - but now you are paying a major performance cost for a lock and a branch on every dereference). That's why I mention the benefit of a GC. You can get safe lifetime access without shared_ptr everywhere (or going all the way to where Rust went and demanding very explicit lifetime annotations for the compiler to use).
>"I've seen enough cases of some stack-allocated string getting passed as a string_view and then blowing up because somebody stored it ..."
Shortest answer - I do not get scared into using particular tools just because they're "safe" and in practice I've never had to deal with the problems you mentioned in my products. There are also various tools that catch these kinds of errors for C++ and other languages. I do use those them tools ;)
A major part of my career has been to write these tools. There's a reason why none of them are sound. I do not believe that modern C++ and the surrounding tool ecosystem is remotely capable of preventing UAF bugs in complex applications, which is a big problem for many applications given the potential consequences of incorrect memory access.
You might belong to the rarified class of uber-developers who can write bug-free C++ applications, but if that class of people exists it is a small one.
>"A major part of my career has been to write these tools. There's a reason why none of them are sound."
This is an interesting take coming from a person who writes the tools. Obviously you know way more about it. I do understand that if I for example link ASAN and it does not show errors for a while it is not a guarantee. The software just might not hit that error path. But I am a practical man running my own company and can not be a perfectionist. If software passes my own test, analyzers and other tools do not report anything suspicious and I have no bug reports from a customer I can sleep well and count my money ;)
>"You might belong to the rarified class of uber-developers who can write bug-free C++ applications"
I absolutely do not write bug free applications but I tend to have very few to begin with and I do eradicate them pretty fast. Some of my stable releases run for years without any bug related complaints. Not sure if that qualifies for "uber". I am however very good architect and deliver solutions in very diverse areas. Desktop, multimedia, enterprise backends, middleware, device control firmware etc. Also understand electronics.
I'm on the static side. The sanitizers are indeed great tools but they are mostly too performance-costly to run in production environments, so they just exist for your tests and maybe for some small percentage of your production environment. Deployed in this manner, it isn't going to catch everything.
I think that asan is also theoretically unsound (I don't know its innards well enough) based on how it actually tags pages but in practice this isn't super relevant. The question is whether you actually execute the problematic sequence in some build that has asan enabled.
> Not sure if that qualifies for "uber".
Writing C++ code with minimal memory errors would qualify as "uber", given the data available from both academic and industrial research on C++ application development. The data is very clear - real applications run into these issues with frequency even when they consistently use modern smart pointers.
Current project (business backend server in C++) that would qualify for attack sits behind NGINX. Looking at the logs I see all kinds of attempts related to Wordpress, PHP, etc. etc. Also we use 3rd party protection on production before NGINX.
Since the server exposes proprietary JSON API with what I would say extreme validation before trying to actually do anything nothing that seems to be illegal gets in.
I mean it is not 100% guarantee as nothing else in our lives but so far nothing extraordinary (keeping my fingers crossed).
if you see it in the logs, that seems like bots looking for low hanging fruit. what about focused attacks by sophisticated adversaries? here, a heap buffer overflow in a c++ library is a major part of enabling rce https://en.wikipedia.org/wiki/Xpdf
shared_ptr use is very rare in good modern C++. Uaf bugs are much rarer. Most people using modern C++ never have any, see any, or risk any. So, no, they would not be better off with GC.
That's true. But if you do the preferred thing of using unique_ptr then you still happily access memory beyond the lifetime of an object via some bug.
> Uaf bugs are much rarer.
This is not true. You can go look at CVEs for large projects like Chrome that have teams of people trying to weed out these kinds of issues and still see UAFs where absolutely nothing is allocated with "new."
Basically if your C++ function takes a pointer you need to write one for unique_ptr, or raw pointer (documenting the ownership).
With a GC, there is a global owner so you don't have to think about ownership for everything, this breaks down for some resource release (maybe half of objects) which is not necessarily easier. But typically GC languages also allow you to use RAII, though maybe not as consistently as C++.
What kind of programs can have unclear ownership? Basically everything that doesn't need to be high quality.
RAII amounts to reference counting which means you need to handle circular references yourself. A garbage collector should handle them automatically. In addition to memory, GC's can catch other unused resources like unclosed file handles.
Arena allocation is useful when many objects have the same known lifetime. An example from games is per-frame allocations. All objects needed only for the frame being rendered are carved out of a large chunk of preallocated memory. The chunk is reused every frame instead of being freed back to the system allocator. In addition to the performance benefits, this results in less memory fragmentation.
I can think of a few cases in which GC would really simplify things, both in C++ and in Rust. I want to do some serious D and Go (and Crystal?) some day to see how it changes life.
Crystal is interesting, but lack of incremental compilation seems like it forces a fundamental limit on how large the projects you're working on should be. Am I wrong here?