9 ms·
> In my experience, the opposite of what the author claims is true: modern C++ leads to code that's easier to understand, performs better and is easier to main
by namelezz 10y ago
> In my experience, the opposite of what the author claims is true: modern C++ leads to code that's easier to understand, performs better and is easier to maintain.
Can you comment on the memory safety of modern C++? I am wondering if I should learn Rust or modern C++.
- PeCaN 10y agostd::unique_ptr, move constructors, etc and you're pretty safe, but it's still not in the same league as Rust. In my opinion you should probably learn Rust unless you want to get a job writing C++ (e.g. game development).
- pcwalton 10y agoI never quite understood why unique_ptr and move semantics are supposed to improve memory safety over new and delete. They reduce leaks, sure, since the compiler inserts free for you at a hopefully-useful place. But you still effectively have to decide when to free, and there is no protection against dangling iterators, references, or pointers. From a security point of view, use after free is far worse than leaking, since UAF can lead to remote code execution. In fact, move semantics create a new type of hazard that wasn't in earlier versions of C++: dereference of a unique ptr after moving it.
- gpderetta 10y agoThey do help somewhat by making ownership more explicit, i.e. they are hints that the programmer can use to do the kind of analysis that rust would do automatically. Dereferencing a moved-from unique_ptr is UB, but many compilers do abort in their hardened modes. edit: spelling
- lbrandy 10y ago> I never quite understood why unique_ptr and move semantics... It's not those types, in particular, it's because C++11 allows you move your ownership model into the type system. And then, and this might sound familiar, writing code cognizant and explicit in its ownership semantics leads to safer, cleaner code.
- fauigerzigerk 10y agoTrue, but if you use smart pointers for that purpose, you cannot at the same time use references to protect against null pointers.
- pcwalton 10y ago> It's not those types, in particular, it's because C++11 allows you move your ownership model into the type system. But it doesn't ensure that you use it correctly. Most use-after-free isn't the result of not understanding the ownership semantics; it's the result of understanding it but forgetting to handle some edge case or another.
- lomnakkus 10y ago> I never quite understood why unique_ptr and move semantics are supposed to improve memory safety over new and delete. [...] Though it isn't the type of safety you're thinking of, if you use make_unique/make_shared you'll also be exception-safe... which is a very nice benefit.
- dragontamer 10y ago> effectively have to decide when to free No. You just pass around the unique_ptr. Don't actually pass a reference to the object. When a function is done using the unique_ptr, it gets returned to its parent object. You'll never have a use-after-free if you use unique_ptr correctly. As others have noted: make_unique() and pass it around with movement semantics. The worst that can happen is that you dereference NULL (use a unique_ptr after it has been sink'd to another function).
- pcwalton 10y ago> You'll never have a use-after-free if you use unique_ptr correctly. As others have noted: make_unique() and pass it around with movement semantics. This is far too limiting to handle anywhere near every real world use case. For example, you can't really call any non-&& methods on the referent following the discipline you propose, because "this" is a raw pointer!
- dragontamer 10y agoYeah, so use shared_ptr and weak_ptr in those cases. Shared_ptr is basically equivalent to C# and Python garbage collection (loops never get collected). So that should be "good enough" for the generic case. unique_ptr is supposed to be used in a very limited fashion as I described. Its more efficient than shared_ptr, but much much more limited.
- pcwalton 10y agoJust to be clear, you're suggesting to use shared_ptr for every object you want to call methods that don't take "this" by move on? (Which is not enough by any means to ensure memory safety, of course…)
- dragontamer 10y agoObviously, full memory safety is not ensured if you ever move over to raw pointers. But if you're copying shared_ptrs around, the reference counting almost always ensures that the memory is valid. The idea is to rarely use references or raw-pointers unless you know you have to.
- deleted 10y ago[deleted]
- AnthonyMouse 10y agoclass fail { foo * p; void init() { p->some_init(); if(p->some_error()) { delete p; throw some_exception(); } } public: fail() { p = new foo; init(); } ~fail() { delete p; } void reinit() { init(); } }; vs. class ok { unique_ptr<foo> p; void init() { p->some_init(); if(p->some_error()) { throw some_exception(); } } public: ok() { p = make_unique<foo>(); init(); } void reinit() { init(); } };
- pcwalton 10y agoWhat fraction of security-sensitive UAF bugs in the real world have had to do with exception safety?
- AnthonyMouse 10y agoIt isn't limited to exception safety. The problem previously had been that there was no clear answer to the question, when should "delete" (and "~foo()") be called for a pointer? If you do it before some other code expected you to then you have UAF. If you do it in more than one place then you may have double free (and two calls to ~foo()). Now the answer is that unique_ptr will do it when the pointer itself goes out of scope.
- pcwalton 10y ago> Now the answer is that unique_ptr will do it when the pointer itself goes out of scope. And my claim is that this doesn't effectively reduce UAF. It doesn't eliminate dangling references, etc.
- AnthonyMouse 10y agoIt seems like you're making the perfect the enemy of the good. Look at my example above. The problem isn't just exception safety, the first class is five kinds of catastrophe waiting to happen. If the user calls reinit() then init() may delete the pointer (because it was written expecting to be called during construction). So now the object exists but the pointer is invalid and any further use of the object will be UAF. Even if the caller understands the exception to mean that the object should not be used anymore, the ~fail() destructor is going to call the ~foo() destructor again and double free anyway. On top of that, the default copy and move constructors and assignment operators for a naked pointer just copy the pointer, which produces UAF as soon as the first copy to be destroyed frees the pointer the others still hold. None of that happens with the unique_ptr version. No explicit call to delete is required so we lose that opportunity to accidentally call it before the surrounding object ceases to exist. The default move constructor actually works and the default copy and assignment operators are deleted, so attempting to copy without an explicit deep copy implementation becomes a compile error instead of runtime UAF. And it's less code too. It can't actually stop you from having dangling references because if it did then it wouldn't be able to compile existing code anymore. But how is it not an improvement over the status quo?
- stagger87 10y agoRust dev baiting in a C++ thread...
- bogomipz 10y agoIs the general feeling that Rust is largely going to be the future for programming using a statically typed language?
- mrich 10y agoTotally depends on what you want to use your programming skills for. In case you are just writing programs for yourself, or even starting a company writing the software from scratch with a handful of people you might as well learn Rust. If you want to get hired with many jobs to choose from, need to interface with/use lots of third party code or generally need to maintain legacy software C++ is a sure bet.
- jupp0r 10y agoLearning Rust actually helped me understand what's considered good C++ memory management design nowadays.