Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> since C++11 there is no reason for not using smart pointers

There are many reasons for not using smart pointers, first amongst them for me being performance.

Smart pointers do allow you to remove a large class of memory bugs from your code (albeit not memory leaks) in the same way that Rust's safe references do. However C++ smart pointers impose a run time performance penalty on your code, whereas in Rust the checks happen at compile time leading to better runtime performance.



> However C++ smart pointers impose a run time performance penalty on your code.

i'm not sure if this is the case. unique_ptr doesn't really do much other than use the type system to ensure that only one instance of the pointed-to value exists at once. as far as i understand, it doesn't strictly do anything at runtime.


For unique_ptr, in a release build, no: but in a debug build there's overhead, and more to the point, stepping into the deference in gdb/lldb at least in my experience requires stepping through the internals, to the point some of the code we use with them is #ifdeffed hackily to use raw ptrs in some cases to avoid this annoyance.

For shared_ptr, the atomic ref counting can cause surprising overhead due to contention in multi-threaded scenarios, even if just accessing the pointer, depending on how the shared_ptr is passed through functions...


There are settings you can place in .gdbinit to avoid stepping in to internals of anything you don’t want to step into, be it std library classes/functions or your own code.

Much nicer than hacky ifdefs.


> There are settings you can place in .gdbinit to avoid stepping in to internals of anything you don’t want to step into, be it std library classes/functions or your own code.

This is exactly what I need. For both, C++ and Rust smart pointers.

Can you share any more info on how to set this up?


On mobile at the moment so can’t check my .gdbinit, but a quick search turns up this page which seems relevant:

https://sourceware.org/gdb/onlinedocs/gdb/Skipping-Over-Func...


unqie_ptr have overhead. It need to run its dtor etc.

https://www.youtube.com/watch?v=rHIkrotSwcc

At 18 min.


> unqie_ptr have overhead. It need to run its dtor etc

Technically it is correct, it causes an extremly light overhead due to the inability of the compiler to optimise a part of it.

In practice, even in HPC, I never notice it to have an impact ever.

And as an advise for any new C++ programmers: Please JUST use unique_ptr, everywhere, all the time...

Trying to manage the lifetime manually with new/delete in modern C++ is not worth the cost in 99.99% of the case. If the impact of unique_ptr is noticeable in your program, you are very likely in a realm where you should not even doing memory allocation anyway.


You're misinformed. Overhead is not about the running the dtor, unless you're willing to leak the memory in your code regardless of the smart ptr usage or not.

What the "overhead" of unique_ptr is usually attached to is the inability for a compiler to pass the unique_ptr as a value through a register but will instead have to use the stack (memory). And that even doesn't apply in the general case but _only_ for unique_ptrs holding an object with non-trivial copy-constructors or non-trivial destructors. This is due to the platform ABI and not the C++ compiler limitation.

Anyway, calling that an overhead is a far stretch and almost purely theoretical unless someone is able to measure the negative effect of such code transformation in the real-world codebase. And I say this as not particularly heavy user of smart pointers.

Also, many other codegen transformations will not fit into a very limited amount of ABI registers so should we argue about not using those as well?

In my opinion, this was only a "campaign" of Google trying to use a unique_ptr as a leverage to persuade the committee to accept their break-the-world ABI suggestion. We know how it all went.


Ah ok interesting I need to dig into this.

Ye well I knew the overhead was really small.


I'm honestly not sure: can unique_ptr<T> be used in place of a T* pointing to a stack-allocated object?


Yes. You can provide a stubbed out deleter or a stack based allocator. But you're then responsible for making sure the unique_ptr doesn't outlive that stack memory. Though that's true for any reference to stack memory.


What's the overhead of an unique_ptr vs the equivalent rust pointer (yes yes, I know that the Itanium ABI has non optimal calling convention for unique ptrs, but I would be surprised if you can measure it)?


Just a thought: null pointer check before deallocation


Aside from you being completely uninformed because free(nullptr) or, for that matter, delete nullptr does absolutely nothing and is well defined operation so you don't need to check for that condition in the first place.

But even if you had to, what implications would it have, if you care to explain?


The implications would be checking if it is null before performing the deallocation of the memory, which is a runtime overhead.

Stack overflow says "delete" would check for null before deallocation: https://stackoverflow.com/questions/4190703/is-it-safe-to-de...

Happy to be educated


Extra branch which is going to be taken 99.99999% of the time is not going to present any runtime overhead. CPU BP unit handles it for us.

That said, if this really had been an overhead, virtually every language out there would suffer from it, including Rust. Every language out there at some point needs to call into the libc.


While I agree with your point overall, it's important to note that libc is definitely not a must on some platforms. On Linux in particular, the Kernel user-space ABI is stable, so you can write your own system calls in whatever language you want.

Also, even if you chose to use some libc for system calls, there is no reason to use malloc()/free(). You only need mmap() or similar, and then your own language runtime can write their own user-space memory manager.

As a side-note, on Windows, libc isn't even a system library. The official way to execute system calls is using win32, and things like malloc()/free() are simply wrappers around HeapAlloc() and HeapFree().


A bit of research revealed that you don't have to call into libc. You may use alternatives or even use sys calls directly, like Go apparently did: https://stackoverflow.com/questions/41720090/does-go-depend-...

PS: small overhead is technically still overhead; yet, I am not saying you should worry about it


Yes, you can write your own runtime ... which will have to rely on the OS internals to do any meaningful work, and in case of memory allocation or deallocation it will have to rely on brk/mmap and then you will be back to square one.

Overhead is almost purely theoretical and it cannot be avoided and is not anything common or specific to unique_ptr's.


Rust could avoid it through the Box abstraction that afaik assumes not having a nullptr


It would not. Rust needs ASAN/UBSAN/TSAN/MSAN sanitizers for a reason.


Can you point me to any significant primarily rust codebase that runs sanitizers? I've never heard of that. Unless you mean miri.


Not miri. I mean the actual ASAN, UBSAN, TSAN, MSAN and LSAN sanitizers that are also commonly used in C and C++ codebases. Descendant is the same: LLVM [1]

> Can you point me to any significant primarily rust codebase that runs sanitizers? I've never heard of that

Not sure what do you mean by that, surely there are. At least the ones who are serious about the development. Sanitizers are part of official documentation in Rust [2].

[1] https://github.com/rust-lang/rust/issues/39699

[2] https://doc.rust-lang.org/unstable-book/compiler-flags/sanit...


No idea what is your point.

The compiler is happy to compile without sanitizers enabled. Why should it require to implement a null check when deallocating when it doesn't when dereferencing?


delete of a null pointer is well defined.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: