Thanks again Yubico for sponsoring this video! Go get a Yubikey at www.yubico.com/store/ RIGHT NOW with my offer code LOWLEVEL5 to get 10% off a Yubikey!
The code example at 5:23 hurts to see, but I've definitely seen code like that in the wild before. In this case, Dog should be passed by reference avoiding the whole problem of ownership. main() still owns the Dog, but keeps it alive long enough for do_something to complete its work. In general, a function does not need to take a unique_ptr or shared_ptr as an argument unless it needs to participate in the argument's lifetime. Only when the function needs to extend the lifetime of a shared_ptr (keeping it from being destroyed), or will take ownership of the unique_ptr (destroying it when it's no longer needed) does a smart pointer need to be passed as an argument.
According to the CPP Core Guidelines F.7, you should pass raw pointers or references if the function doesn't change the ownership of your variable. If you apply this rule, then you can easily distinguish owning smart pointers from non-owning raw pointers. Additionally, this makes your function more useful because it will accept a pointer/reference, regardless of how the object has been allocated.
@@loading_wait Most library authors know that pointer can come from new/new[]/pmr/malloc()/stack and therefore it CAN'T be deleted unless they themselves allocated it. And by "most" I mean "probably everyone beside authors of the libs that are used by Low Level Learning and loading_wait, definitely everyone in my last 2 decades of using c++ and not hitting this problem even once" I had more troubles with implicit intfloat conversion than with not knowing if somebody decided to call delete for no apparent reason. This video is bad. Its takes are bad. SeanCline below explained very well how to use.
If the library you're using is written in modern C++ it should be OK since they should follow the core guidelines. The problems arise when you have to call a C library in that case check the doc. But I guess in general, if your gut tells you this function might do something fishy, check the doc.
@@loading_wait true but it's fine if you know that's not your case. It also doesn't seem worth it for me to safeguard against the future possibility if it's not expected.
In their defense, smart pointers are of all tools available to developers but not everyone can adapt to a certain tool. Actually remapping mental model and using smart pointers everywhere in the codebase may be troublesome especially if working on a large project with multiple people in collaboration.
Shared pointers shouldn't be used like that. They exist to share the ownership, in that case u should have used a raw ptr or simply a reference/const reference. Shared pointers add an extra layer of overhead due to the implementation itself; don't use them to move stuff between functions
While the overhead is there, its usually the most insignificant detail, (unless you're dealing with multi-threading, but the standard smart pointers aren't helpful in any of those cases anyway) but yes using a shared pointer like this is not really correct, usually a shared pointer is for complex scenarios where you can't be sure of when the pointer should be freed because of all the things that depend on a valid value, containers being one of the most useful cases.
* Pass reference if value can't be null * Pass raw non-const pointer if it can be null * No, it doesn't defeat the purpose. Don't call delete - leave all memory management to unique_ptr.
There's also span, mdspan, and string_view, so there's even less incentive now to touch raw pointers now for things that smart pointers can't do well. There's also out_ptr and inout_ptr to assist the smart pointers.
@@Spartan322 You can use references for that. If by memory you mean the heap, then you better use the "ownership" concept. Maybe "never" is a strong word there are some situations where they are still useful, like observer pattern or handling dynamic memory within a static variable (although I would personally still use smart pointers here)
@@Megalcristo2 Biggest cases and why you can't really use references is when you need to also return an empty pointer and your class doesn't or can't have a tombstone state. (which usually you don't want if your class consumes more then a pointer size of memory on construction anyway)
You don't need to convert to a shared_ptr here I think. You can take a reference in the function and save the ownership semantics to the passing function. The way I had it described to me is that smart pointers are all about ownership, if you pass a shared pointer to a function you can't expect that memory to be free'd when you get rid of the object in your scope(different if you convert to weak and pass that). But you're saying "hey, function, you take equal ownership to this data type. When we're BOTH done with it then it can be released and only then". Whereas a unique_ptr passing a reference down the call stack is saying "I retain ownership of this memory, you can use it, but you may not affect its lifetime". You still have to be strict with references like you do with normal pointers, but you can express whether or not you're going to be interested in the lifetime of the underlying object or not by using the function signature.
if you don't want to transfer ownership to a different function then in 99.5% of cases the proper way of doing it is taking the parameter by reference instead of by value or move.
This is REALLY BAD ADVICE! You need to make the function take a Dog& (reference). Shared pointers are a last resort and are often a sign of a bad design!
Shared ptrs in general aren't bad design. But using them like this definitely is. You basically only want to pass a shared pointer to a function like this if it then saves that shared pointer somewhere (like in a vector) but you still want the original location to also own it (Though even in those cases using weak_ptr might be more useful)
@@sinom Nope, shared_ptr in general is a sign of a bad design. It makes lifetimes unclear, which makes it harder to reason about your code. Always avoid shared_ptr and prefer unique_ptr. You should only use it when there is no other option. You often see new C++ programmers slap a shared_ptr on everything, while the seasoned C++ professionals pretty much only use unique_ptr.
@@sinommight also add that you can use shared ptrs multiple places but also want to pass it as an argument to a method that does not have the intention of sharing ownership. In that case you would use a const ref shared ptr though. A const raw pointer can also be passed using .get() but will require the whole method to be const, while the const ref shared ptr does not.
I disagree. Sometimes the object is too big, so you need to allocate it on the heap, ideally with a smart pointer. Also, abstracted objects normally only compile when it's allocated on the heap.
0:30 "sizeof(somepointer)" is almost always a bug as well. You're allocating space (and zeroing the memory) for an object, but you're only requesting enough space for a single pointer, rather than whatever type auth actually points to.
You have a pointer. I was thinking auth and service were arrays; but you cant change the address of an array. Must be somebody that doesn't know the purpose of sizeof
maybe we should come up with "dumb pointers" then and call them "std::generic_ptr" and "std::greedy_ptr". or we make pointers based on cool films and series that we watched, like "std::clannad_ptr", a pointer that periodically forgets what it pointed to, and the thing it pointed to suffers from traumatic events in the past
men in c++ smart pointers were there before rust appeared on this earth so what sounds rust ? go and do some research men . this annoying rust you are second one who posted nonsense without doing any kind of research . rust is a shit in and out and it lacks tons of things . it doesn't evne let you do things without using shit impl / struct / trait combined . and without using stupid moronic & ' < > _ and moronic things it has . you are just bunch of web developers who never coded a shit in systems language so go and cry somewhere else : D if you like rust be quit here we are talking about c++ not about crap rust
void do_thing_with_dog(const Dog& dog) { dog->bark(); } auto dog = std::make_unique(); do_thing_with_dog(*dog); There is no need to pass the unique_ptr to the function and the same is also true for the shared_ptr. They should be passed as smart pointers to free functions or class members only when you want to express changes in the managed object lifetime.
@@anon1963 there is a typo in his comment, *dog* is not a *pointer* but a *reference* ... so you can use *.* to access each member void do_thing_with_dog(const Dog& dog) { dog.bark(); }
Code at 0:30 could possibly have more than just one bug. I know it's for demonstration purposes, but I got fixated on malloc and memset that I didn't get to see the use after free part.
In the first code snippet: ```c auth = malloc(sizeof(auth)); ... strcpy(auth->name, line + 5); ``` The `malloc` call does not necessarily allocate enough bytes for the `auth` structure, but merely the 8 bytes for a pointer to it. Depending on the size of the `auth` struct (or rather, the offsets of the `name` and `auth` fields within it), things will go wrong here as well.
Pass 'unique_ptr x' into function with: void myfunc(Obj & x); myfync(*x) I.e. derefence the unique_ptr and pass by reference. All C++ smart pointers have X::get() that just return the plain pointer: If the unique_ptr can be nullptr use void myfunc(Obj * x); myfync(x.get()) instead. However this partly shifts the burden back to programmer: Programmer must now guarantee the Obj memory address does not escape from the myfunc().
This video is kind of bad. You should be const-correct whenever possible for maximum expressiveness, so the gripe at 6:59 is just poor. Don't pass std::shared_ptr by value everywhere when you don't need to; that's seriously overkill, bleeding performance when it's not necessary to, and excludes pointers to objects with static lifetimes from being passed in. Raw pointers are okay to represent non-owning pointers, and well-written code won't be deleting non-owning pointers.
Guy kinda missed the point... Feels like you're using paradigm/style from other languages, not very C++-onic (or whatever the C++ version of Pythonic is).
Idk about how everyone else does things, but generally speaking, my methodology is to use raw pointers in small, contained algorithms in the methods for objects so I know exactly how and where the pointers are being used. I'll then use smart pointers in the more dynamic parts of the program with a ton of objects being created, deleted, and manipulated all at the same time, possibly in a multi-threaded environment. That's when the dynamic behavior of the program gets really difficult to check for every possible situation a pointer might miss the deallocation point. It of course is possible to use raw pointers very well in every method to increase performance, but in a non-intentive program, the stability of smart pointers makes them a very useful tool.
What is the difference between `std::shared_ptr` and pass by reference `&`. From my understanding and also from your word shared pointer is a unique pointer that allows multiple other pointers to point to its data on the heap. i.e many pointers with one data. On the other hand pass by reference is a pointer that points to another pointer that hold the data on the heap. If the pointer that owns that data got cleaned up when it goes out of scope the reference & will point to invaild data. And in c++ the compiler will give error if doing incorrectly.
I think the thing you should have gone over more is how encapsulating them in a class like this allows them to be deconstructed automatically when they go out of scope. I find that when I write C++ I tend to not allocate anything myself and instead I just allow the container classes to handle all of that for me. If I need a custom object I can write the allocation in the constructor and deallocation in the destructor and I'm done. Since the standard container classes call my destructors for me, I don't have to care about allocation most of the time. It's only when I write C that I have to care and I've got methods for dealing with it there, but it's never as easy when I have to create some new "class" to do things with. I don't hate the way I have my data structures library laid out, but I don't love it either and I constantly wish I had operator overloading and real classes like in C++. I don't totally hate C++ and its syntax, just *most* of it.
I doubt code at 0:45 will even run to the point where it uses dangling pointer, because it allocates auth as size of auth, which is size of pointer, and it's clear from the code that auth is some struct that should be atleast 32 bytes long.
That use after free at the beginning isn't the only issue. There's quite a few issues here from basic pointer arithmetic just being wrong and duplicating strings that are not freed. Not to mention how dangerous it is to deal with C strings this way. The line buffer being 128 bytes is the only thing saving you from bad things happening with your already incorrect pointer arithmetic.
Plus, the `malloc` call being supplied a `sizeof(some_pointer_variable)` as its `size` parameter, causing it to allocate 8 bytes regardless of how large the `auth` struct actually is...
@@Uerdue I mean you could get around that by using the struct type name but maybe in this case the struct name is also auth, the full code isn't given so we can't be sure -_-
@@jlewwis1995 I can't think of any way to both typedef a struct and declare a variable of the same name in C. That's just gonna lead to compiler errors. The sizeof usage definitely appears wrong. It probably functions okay because malloc will probably allocate more than the 4 or 8 bytes (pointer size) due to it allocating pages.
I would love to see a video about Arenas. Since they are an allocation strategy that isn't used or talked about often even though it's quite powerful and solves the problem of ownership in a quite unique way.
additionally, the most obvious error with the code was (unless imported by a header file which was not shown in the code) auth & service would be 2 undefined identifiers. i also think that (although i may be wrong) as im unsure of the return type of std::move() u could just define the function to take in a reference to a std::unique_ptr. that is, std::unique_ptr& as apposed to std::unique_ptr
he could, but the idea of std::move() is that it casts the parameter into rvalue reference, which matches to && first.. it's just type system hack to select the correct signature; it doesn't "move" anything it just encourages movement.. LOL.. the correct thing to do in the video's example would just pass by reference or const reference, depending on the intent.. you would only pass shared_ptr by value when you want to co-own it, let's say you have a thread and want the object to remain alive as long as the thread is alive you would capture shared_ptr, then it doesn't matter which owner dies in which order as the last one will turn off the lights. It would been more useful video if it actually illustrated useful use cases for these smart pointer types.
not particularly, unless someone decides to be dumbass and write free(dog).. just pass by reference when the intent is just to do crap with the instance
As a fellow Rustacean this genuinely makes me bad for C++ people. You need so much boilerplate to make a unique pointer and even after all of that the compiler can't catch that it was moved and you still get a segfault
This video makes it seem harder and more error prone. You should check other videos to see that it is not that hard and it is pretty safe if you know what you are doing. I know c/c++ can have memory leak issues but unique_ptr actualy solves most of the problems
When a unique pointer is moved it becomes a null pointer( it stops pointing to the original memory but to the address 0x0000). Additionaly 99% of times you wouldn't move a unique_pointer but pass it by reference
Agree with the comments here stating that's not how unique or shared pointers should be used. I additionally found it a bit sad that it was implied that adding const to the speak function is annoying. It is (because c++ gets the defaults wrong) but it is necessary. Const correctness prevents bugs as it allows us to clearly state when a mutable pointer or ref is needed and when it is not.
It also prevents angry users who get errors out of nowhere (they would expect that a function which doesn't mutate anything would accept their const pointer, but no, the compiler just craps out).
Unique pointers are unique owner, not a the only possible pointer to an object. Passing things by reference/pointer should be the default when a function don't need to deal with ownership.
yeah, should've gone with a reference also, const-ing methods and parameter types is actually really good practice, self documenting code and whatnot in C++ we use the following idiom: void doSomething(const std::string& some_string) to declare some_string as a read-only reference and avoid copying. This is really good practice for performance and readability (yes, const& is very readable after C++ damages your brain sufficiently :)
Said "really good practice" is *REALLY GOOD PRACTICE* , it can be a reason others cuss you out for! Simply put, people often omit that word willy-nilly, which can cause significant errors if even one user of your code uses it.
Raw pointers are essential tools for performance programming. Complexity is just as bad for code as carelessness. Practice using dangerous things so you don't screw up when you need to use them. C and C++ memory management continues to exist for a lot of important reasons.
0:40: I honestly thought you meant that you were applying the sizeof operator to the auth pointer, rather than the *auth object (twice). Therefore, you only allocate 4 or 8 bytes with the malloc, and you also only clear that much with the memset, but then access much more with the strcpy() later. Which is a classic buffer overrun, and source of crashes later. Actually, if you use musl, it will crash at the free() at the latest. BTW: Rather than malloc and memset, just use calloc(), which will return zeroed-out memory. And it may be faster, because calloc() may know that the memory is already zeroed-out.
But the do_something_with_the_dog, isn't that pass by value? Or is it by reference? In C you'd have to pass a double ptr for pass by reference in order to dealloc. You can modify the members, but the free would do nothing. I just forget the different rules in c++ so maybe I am wrong. I get the point though, but was curious if I was wrong
It's usually a pass by copy but unique_ptr can't be copied only moved. That's why std::move is required to pass it, it casts the value into an x value expression (somewhat similar to r value expressions in C but with a few differences), allowing the copy to instead call the move constructor. In general move should only be used when you want to transfer ownership over stuff. In general it's less expensive than a copy but can be more expensive than just using a reference. If in C++ you want to signify something is a reference you can either use pointers like in C (T* param) if you want it to be a nullable reference or use the & symbol instead to say it is a non nullable reference (T& param). Non nullable references can be used without having to dereference the parameters while nullable references (pointers) need to be dereferenced (arrow, *, etc.) (And ofc they should always be null checked in the function since they imply you can pass a nullptr into that function without issues. Pass by reference means it isn't null checked since you can't pass null by reference on accident)
@@sinom but I’m talking about the original function when it’s just a pointer and the last line *delete* is called. It wasn’t compiled or ran so I’m just not sure that would work. Again, totally get the point that the video is getting at, but, like you also explained, it would just send a copy which wouldn’t cause a bug.
@@sinom code that uses std::move can be written without it and needs to be refactored passing unique_ptr by value and returning the same object in the function is the stupidest thing I've ever seen in my life, this video was biased and showed that the language allows this but it would be solved by passing the object itself as a reference or const reference no c++ programmer who knows smart pointers would do what he did
The big thing that I also thing is missed in the advantage of RAII in C++ and smart pointers being a part of that is the ability to reduce cognitive complexity. You can do things like just return in an error case without having to copy paste cleanup a bunch of places or use a go to. You just don’t have to think about clean up so if an input is wrong or whatever you just return instead of having these deeply nested functions that are so common is C code.
I would have actually used a unique_ptr reference in the case you presented for the unique pointer. I also kinda like the "returning the unique pointer back" solution in lots of cases
I'll allow passing shared_ptr by reference when smuggling it to it's final resting place somewhere inside the dark abyss of code that probably could've been written in more sane way to begin with, so when I wrote "I'll allow" I meant to say tolerate just barely.
In the context of what your doing, you shared an object for something small, i'd be fine in that case with passing the raw ptr from the get() method in for that, i treat raw ptrs in C++ as borrowed, not owned so that ptr passed into `do_something_with_the_dog()` would be raw if this was my code.
As a rust user my first thought would just be to pass a mutable reference into the function lol, it's so much easier, you don't even need a smart pointer to do that
@@Phantom-lr6cs rust is losing it's charm as people gain xp writing it, the traction and lure of new and shiny is wearing thin.. heck, I'd rather write Swift or C# than Rust :_D
In the first example, "auth" is only freed if the first five characters of "line" are "reset". "auth" will be used again only if the first five characters of "line" are "login". Aside from the bug of using "sizeof(auth)" instead of "sizeof(*auth)" in the malloc and the subsequent memset, there is no problem with use after free.
Shared pointers are great, but you've definitely gotta use them judiciously. They come with overhead, and it's easy to leave one in a place where it will never get cleaned up if you're not careful.
@@MI08SK or passed by value; the reference count must be thread-safe so there is either mutex or more likely some tricky atomic compare-exchange or linked-load/store contraption that is cheap most of the time except when multiple threads hit the bus lock at the same time, then caches will be busy synchronizing.. more cores.. less speed..
If a function is statically typed to Dog (or any sort of pointer to it), then it might as well be a method of Dog. That's besides the point though - your examples still make perfect sense for generic functions or functions typed to interfaces, without being cluttered. Inicidentally I was preparing myself to rant about Yubico yesterday, but I was pleasantly surprised by how easy it was to uninstall their "code container". To their credit, they did that right.
Does .get() defeat the whole point of smart pointer. No it does not!! The whole video seems to be based on the premise that raw pointer bad. No raw pointer is a perfectly fine model of a non owning reference. Forbid junior to use new and delete, and you are golden.
unique_ptr came to the c++ STL before rust was even introduced. And may I ask, what do you think is so burdensome about it? Having to type 7 extra characters (as opposed to Box)? Otherwise the functionality is identical, readily available in the standard library, and even with extra features (that might not be present in rust or are behind its unsafe firewall). Regarding your ‘mimicking’, I think you might have gotten it the other way around.
@@mariansalam @mariansalam The quote's a meme, don't take it too literally. Though if you want to know why I personally prefer rust's approach, it is because it also gives compile-time checks for ownerships errors. This catches things like what is seen in the video at 4:30
@@mariansalam Rust does not let you move the ownership of data or unique smart pointer inside a function while allowing usage of that data after the function has been called without compiler errors. That is the power C++ can only dream of, not the extra characters you don't need to type.
3 месяца назад
couldn't you declare the Dog parameter as reference& or unique_ptr doesn't allow conversion to reference variable?
True! 👍👍 People should be more careful while using pointers. It's not a disadvantage. Pointers provide low-level controls, so it's a feature. I'm talking about pure C. A bit of proper attention eliminates the majority of memory-related bugs in C.
@elcugo it's why your operating systems and drivers work. Good habits work. However they require discipline and professionalism. Most managers have too much heart to fire the guys who have neither. Once again the fish rots from the head. Engineering is not easy. It is in fact detail oriented and hard. The first real computers were designed programmed and maintained by mechanical engineers and machinists... if they didn't hit their numbers on their lathe then the gun director track solution was wrong and that kamikaze killed you and everyone who screwed up the "program". They were properly motivated and their guns usually didn't miss if the radar was online and working properly. The make no mistakes approach works just fine for real engineers that build serious tech.
@@robmorgan1214 That's a lot of nonsense. People have been killed by engineering mistakes multiple times, millions of dollars lost for memory bugs. Bridges have fallen, rockets exploded. "Real engineers" build tolerances into their processes, because they know mistakes will happen. Important products gets tested by months before they get released. You are stupidly and dangerously wrong.
0:33 yes yes I was... I treat any free/delete preceding the usage of the freed/deleted object as a use after free bug weather or not it is checked for... the correct way to write that is have all of the other if statements return/continue then if I reach the bottom of the function/loop I assume that they wanted a reset ... this also fixed the issue of not handling malformed inputs which is another bug in the code (albeit a non critical 1)... actually depending on what the input is used for and how it is gathered I may not omit reset's if statement and out put an error on malformed input...
also for the sake of readability everything in that while loop should be in its own function (named something like parseServiceCommandFromInuput [I too suck at function naming it is ligit one of the hardest things in programming]) if I were trying to understand what your app does I don't necessarily care what commands you're parsing for there for it would take less time for me to realize that is what you're doing if you just tell me that is what you're doing... also having common patterns like that in [well named] functions helps with debugging because if you follow the "functions only do one thing. And that one thing is only done in that function" rule that limits the number of places I would have to look for bugs...
0:27 The bigger bug I see is that #includes for , and , and declarations for auth and service are missing, so the code wouldn't even compile to have a bug... 1:58 One would never try to do that in C; if you have ownership of the pointer, you must either pass it on or destroy it; if you don't, you do not kill it under any circumstances. Simple as that.
The whole point of the unique_ptr is so that it makes memory management easier as compared to raw pointers. So the pointer getting freed after a functional stack gets deleted (as the deconstructor of the unique_ptr is called) is not an inconvenience, but it's instead a feature because unlike traditional pointers in heap that may remain in the memory even after going out of scope, unique_ptr make sure this doesn't happen. But i understand the problem which is what if i want to save it from getting deleted in a function so that I can then use it again after the functional stack is deleted.
I feel like examples against raw pointers are a bit synthetic. The first example is just bad code, don’t use sequential ifs when you mean to do a choice from multiple options (switch or else if chain). The second example is just bad practice as well. Don’t delete an object that you got as a pointer parameter. If you have to use a raw pointer, just let the object die with its scope. If you know in advance you’ll need to transfer ownership, then purposefully use a smart pointer. No need to bloat up the code just because someone else might be coding dangerously.
The function should have taken a reference, possibly a weak_ptr if it needed to keep a long standing reference but it definitely shouldnt be taking a full on shared_ptr since it should NEVER take ownership proper just look into the pointer
Anyone gonna mention you can pass unique_ptr by reference? Unless you need raw pointer behavior too, with unique_ptr you should prefer passing a unique pointer by reference, if you need nullability otherwise use raw pointers, else use references.
No. This video really isn't doing a lot of stuff correctly. If you want to pass a unique_ptr to a function you need to ask yourself. Do you want that function to then own that unique_ptr or does it not need to own it at all. Ownership is usually only relevant if e.g. you're then gonna add it to a vector, member of some struct etc. if like this you just want to call a function on that object in the unique_ptr then what you actually want to do is pass it by reference instead of value. so instead of using function(unique_ptr ptr) { ptr->do_something(); } you want to do function(unique_ptr& ptr) { ptr->do_something(); } (preferably even const ref if possible) No need for shared_ptr for simple stuff like this shared_ptr is only necessary when for example you have two different vectors that both need to have objects they share between them
Well, raw pointers are not that bad as long as you know what is actually going on in your code. Make the program too complicated or allow random people to mess with it without knowing what they do - anything will fail. I think maybe C++ is not perfect for certain tasks. Maybe, in some scenarios - you just can't avoid it being unsafe. And every effort to mitigate it makes it more complex, and the more complex it is, the probability of error increases. Now the case where "unsafe" is "safe": a block of code. Relatively small, readable, doing one thing, invoked in a few exactly known places. Made to be as efficient as it gets. If you test all the edge cases (because it's possible for certain scenarios) - it is SAFE. And probably worth doing. When you go into more variable territory - we're getting less and less sure if the new added complexity REALLY pays off.
But why doing soemething like you did: do_something_with_the_dog(ralf); instead just this: ralf->do_something_with_the_dog(); ??? You could remain unique_ptr and still have void and not need to pass dog to the dog's class
I hate the behaviour of adding values to pointers in C... why does adding one add the size of the pointer's data type? I hate having to cast to uintptr_t every time I do pointer math.
Because that's way more logical than what you want... also, if you really want to misalign the pointer, you should be casting to (char *) (or its signed or unsigned variants) instead of (uintptr_t); the latter is for getting the address out of a pointer.
You should almost never directly call new or delete if you use smart pointers. With that rule passing in a raw pointer is fine if you want to do a null check, but that function should have taken a Dog reference. Using shared pointer is a horrible idea unless you actually need the functionality it provides. The performance overhead of reference counting is huge. Passing smart pointers between functions should be only done as a sign of explicit ownership transfer.
I always use raw ppinters, not smart pointers and my code never crashes. If you write correct code without memory leaks, raw pointers are fine to use and much less complicated than smart ones.
You forgot the pass for reference. Passing for reference the smart pointer to the function do_something_with_the_dog work and let the responsability of the resource ralf in the main scope. Nice work and nice effort. Keep pushing!
Bad example. Should have just passed Dog as a reference like Dog & d or const Dog & d. A function that operate on an object should not care about if it is a pointer or not. When you call the func, just dereference it. Also should not pass shared pointer to a function like that, the function does not own anything. Doing this also incur unnecessary performance overhead from the atomic reference counting. Okay if you do it less frequently, but I would never do this in any function that will be called hundreds of times or more.
Call me dumb, bt I fell like practicing some good pointer habits is much better than using smrt pointer. Such complex syntax alone can cause serious bug. After all, you gotto manually check and ensure more stuff with smart pointer than regular raw pointer.
Unique pointers _are_ Rust's way (ownership and borrow semantics) but C++ just defaults to being unsafe for compatibility. Likewise, shared pointers are the same as using Rc in Rust. I definitely prefer the Rust syntax though it's way less verbose!
@@seabrookmx The "Rust's way" happens at compile time without overhead during runtime unlike with C++ if you use smart pointers whose references will be counter during runtime. If you opt for speed by not using smart pointers with C++, you'll pay for it in terms of potential runtime crashes. If you explicitly use Rc in Rust, then you are on the same line with C++'s smart pointers in terms of speed. In Rust you still pay for the complexity of dealing with the borrow checker, which may or may not be a problem dependening on the developer.
lolz kids : D why are you bunch of rust clowns here doing ? go and code in that crap : D you think this is exists in c++ cause of rust shit ? in c++ smart pointers were there before rust shit appeared : D go and cry for your rust no one cares :D cuz its dumb and shit syntax and crap langauge where you need 15 mb for just hello world and compilation takes forever : D
Or you just always set unallocated pointers to null and always check for null before using and set to null after freeing it. Also be careful when reassigning it not to leave dangling unused memory.
Thanks again Yubico for sponsoring this video! Go get a Yubikey at www.yubico.com/store/ RIGHT NOW with my offer code LOWLEVEL5 to get 10% off a Yubikey!
The code example at 5:23 hurts to see, but I've definitely seen code like that in the wild before.
In this case, Dog should be passed by reference avoiding the whole problem of ownership. main() still owns the Dog, but keeps it alive long enough for do_something to complete its work.
In general, a function does not need to take a unique_ptr or shared_ptr as an argument unless it needs to participate in the argument's lifetime. Only when the function needs to extend the lifetime of a shared_ptr (keeping it from being destroyed), or will take ownership of the unique_ptr (destroying it when it's no longer needed) does a smart pointer need to be passed as an argument.
Really helpful explanation
Was looking for this comment. Use references to keep ownership just like in Rust
Totally agree with this explanation. We don't need add complexity when it can be avoided and still get the same result.
I was curious about why not use pass by reference. This comment explains everything.
Low Level Learning is a C programmer, that's why he doesn't know how to correctly use smart ptrs.
According to the CPP Core Guidelines F.7, you should pass raw pointers or references if the function doesn't change the ownership of your variable.
If you apply this rule, then you can easily distinguish owning smart pointers from non-owning raw pointers.
Additionally, this makes your function more useful because it will accept a pointer/reference, regardless of how the object has been allocated.
You still run into not knowing if deeply nested functions (within a library you didn't write for example) ever try to drop.
@@loading_wait Most library authors know that pointer can come from new/new[]/pmr/malloc()/stack and therefore it CAN'T be deleted unless they themselves allocated it.
And by "most" I mean "probably everyone beside authors of the libs that are used by Low Level Learning and loading_wait, definitely everyone in my last 2 decades of using c++ and not hitting this problem even once"
I had more troubles with implicit intfloat conversion than with not knowing if somebody decided to call delete for no apparent reason.
This video is bad. Its takes are bad. SeanCline below explained very well how to use.
If the library you're using is written in modern C++ it should be OK since they should follow the core guidelines. The problems arise when you have to call a C library in that case check the doc. But I guess in general, if your gut tells you this function might do something fishy, check the doc.
It’s always best to follow the guidelines
@@loading_wait true but it's fine if you know that's not your case. It also doesn't seem worth it for me to safeguard against the future possibility if it's not expected.
"smart pointers" actually more intelligent than 50% of the programmers
Not than I.
In their defense, smart pointers are of all tools available to developers but not everyone can adapt to a certain tool. Actually remapping mental model and using smart pointers everywhere in the codebase may be troublesome especially if working on a large project with multiple people in collaboration.
so just "above average pointers"
My profesor once said that strings are more complicated than a certain fraction of people present at an undisclosed meeting.
1 bjarne == 100 programmers
Shared pointers shouldn't be used like that. They exist to share the ownership, in that case u should have used a raw ptr or simply a reference/const reference. Shared pointers add an extra layer of overhead due to the implementation itself; don't use them to move stuff between functions
While the overhead is there, its usually the most insignificant detail, (unless you're dealing with multi-threading, but the standard smart pointers aren't helpful in any of those cases anyway) but yes using a shared pointer like this is not really correct, usually a shared pointer is for complex scenarios where you can't be sure of when the pointer should be freed because of all the things that depend on a valid value, containers being one of the most useful cases.
* Pass reference if value can't be null
* Pass raw non-const pointer if it can be null
* No, it doesn't defeat the purpose. Don't call delete - leave all memory management to unique_ptr.
There's also span, mdspan, and string_view, so there's even less incentive now to touch raw pointers now for things that smart pointers can't do well. There's also out_ptr and inout_ptr to assist the smart pointers.
I have a better one:
Never use raw pointers
@@Megalcristo2 You still have to use them for non-owning single instances in memory.
@@Spartan322 You can use references for that. If by memory you mean the heap, then you better use the "ownership" concept.
Maybe "never" is a strong word there are some situations where they are still useful, like observer pattern or handling dynamic memory within a static variable (although I would personally still use smart pointers here)
@@Megalcristo2 Biggest cases and why you can't really use references is when you need to also return an empty pointer and your class doesn't or can't have a tombstone state. (which usually you don't want if your class consumes more then a pointer size of memory on construction anyway)
To add to this: see also the C++ Core Guidelines, which lay out ground rules for how to pass and use smart pointers.
Heaphone guy metal571 also in low level programming ??
Damn the world is small :)
Yeah this is my full time real job of being a C, C++, and Python senior software engineer
You don't need to convert to a shared_ptr here I think. You can take a reference in the function and save the ownership semantics to the passing function.
The way I had it described to me is that smart pointers are all about ownership, if you pass a shared pointer to a function you can't expect that memory to be free'd when you get rid of the object in your scope(different if you convert to weak and pass that). But you're saying "hey, function, you take equal ownership to this data type. When we're BOTH done with it then it can be released and only then".
Whereas a unique_ptr passing a reference down the call stack is saying "I retain ownership of this memory, you can use it, but you may not affect its lifetime". You still have to be strict with references like you do with normal pointers, but you can express whether or not you're going to be interested in the lifetime of the underlying object or not by using the function signature.
if you don't want to transfer ownership to a different function then in 99.5% of cases the proper way of doing it is taking the parameter by reference instead of by value or move.
This is REALLY BAD ADVICE! You need to make the function take a Dog& (reference). Shared pointers are a last resort and are often a sign of a bad design!
Shared ptrs in general aren't bad design. But using them like this definitely is. You basically only want to pass a shared pointer to a function like this if it then saves that shared pointer somewhere (like in a vector) but you still want the original location to also own it
(Though even in those cases using weak_ptr might be more useful)
@@sinom Nope, shared_ptr in general is a sign of a bad design. It makes lifetimes unclear, which makes it harder to reason about your code. Always avoid shared_ptr and prefer unique_ptr. You should only use it when there is no other option. You often see new C++ programmers slap a shared_ptr on everything, while the seasoned C++ professionals pretty much only use unique_ptr.
@@sinommight also add that you can use shared ptrs multiple places but also want to pass it as an argument to a method that does not have the intention of sharing ownership. In that case you would use a const ref shared ptr though. A const raw pointer can also be passed using .get() but will require the whole method to be const, while the const ref shared ptr does not.
I disagree. Sometimes the object is too big, so you need to allocate it on the heap, ideally with a smart pointer. Also, abstracted objects normally only compile when it's allocated on the heap.
This answer is genius.
0:30 "sizeof(somepointer)" is almost always a bug as well. You're allocating space (and zeroing the memory) for an object, but you're only requesting enough space for a single pointer, rather than whatever type auth actually points to.
I noticed that too. The correct syntax would be `sizeof(*somepointer);`.
There's also a huge lack of #includes and declarations there...
You have a pointer. I was thinking auth and service were arrays; but you cant change the address of an array. Must be somebody that doesn't know the purpose of sizeof
I dont understand why u did all of these things. You could just pass by reference or pass by ptr.
trueeeee
exactly
maybe we should come up with "dumb pointers" then and call them "std::generic_ptr" and "std::greedy_ptr". or we make pointers based on cool films and series that we watched, like "std::clannad_ptr", a pointer that periodically forgets what it pointed to, and the thing it pointed to suffers from traumatic events in the past
"std::clannad_ptr" makes me want to cry.
That just sounds like Rust with extra steps
men in c++ smart pointers were there before rust appeared on this earth so what sounds rust ? go and do some research men . this annoying rust you are second one who posted nonsense without doing any kind of research . rust is a shit in and out and it lacks tons of things . it doesn't evne let you do things without using shit impl / struct / trait combined . and without using stupid moronic & ' < > _ and moronic things it has . you are just bunch of web developers who never coded a shit in systems language so go and cry somewhere else : D if you like rust be quit here we are talking about c++ not about crap rust
void do_thing_with_dog(const Dog& dog) { dog->bark(); }
auto dog = std::make_unique();
do_thing_with_dog(*dog);
There is no need to pass the unique_ptr to the function and the same is also true for the shared_ptr. They should be passed as smart pointers to free functions or class members only when you want to express changes in the managed object lifetime.
is this even correct? Dog is not a pointer now and how do you call -> on it?
@@anon1963 there is a typo in his comment, *dog* is not a *pointer* but a *reference* ... so you can use *.* to access each member
void do_thing_with_dog(const Dog& dog) { dog.bark(); }
@@ensuretime Yeah, besides the obvious slip this is the way..
Thanks ! I encountered this issue at work today and this helped me validating my decisions and my understanding
Code at 0:30 could possibly have more than just one bug. I know it's for demonstration purposes, but I got fixated on malloc and memset that I didn't get to see the use after free part.
LOL the lack of declarations for auth and service sold it for me, and then the lack of #includes as well...
and sizeof potentially taking sizeof the pointer, hard to say when all of the declarations were missing,
In the first code snippet:
```c
auth = malloc(sizeof(auth));
...
strcpy(auth->name, line + 5);
```
The `malloc` call does not necessarily allocate enough bytes for the `auth` structure, but merely the 8 bytes for a pointer to it. Depending on the size of the `auth` struct (or rather, the offsets of the `name` and `auth` fields within it), things will go wrong here as well.
Pass 'unique_ptr x' into function with: void myfunc(Obj & x); myfync(*x)
I.e. derefence the unique_ptr and pass by reference.
All C++ smart pointers have X::get() that just return the plain pointer:
If the unique_ptr can be nullptr use void myfunc(Obj * x); myfync(x.get()) instead.
However this partly shifts the burden back to programmer:
Programmer must now guarantee the Obj memory address does not escape from the myfunc().
This video is kind of bad. You should be const-correct whenever possible for maximum expressiveness, so the gripe at 6:59 is just poor. Don't pass std::shared_ptr by value everywhere when you don't need to; that's seriously overkill, bleeding performance when it's not necessary to, and excludes pointers to objects with static lifetimes from being passed in. Raw pointers are okay to represent non-owning pointers, and well-written code won't be deleting non-owning pointers.
Learning these aspects of cpp as a rust learner makes me appreciate their choices with the borrow checker and lifetimes.
Guy kinda missed the point... Feels like you're using paradigm/style from other languages, not very C++-onic (or whatever the C++ version of Pythonic is).
But fast as fuck in comparison with python xd, isn’t that the point?
Idk about how everyone else does things, but generally speaking, my methodology is to use raw pointers in small, contained algorithms in the methods for objects so I know exactly how and where the pointers are being used. I'll then use smart pointers in the more dynamic parts of the program with a ton of objects being created, deleted, and manipulated all at the same time, possibly in a multi-threaded environment. That's when the dynamic behavior of the program gets really difficult to check for every possible situation a pointer might miss the deallocation point. It of course is possible to use raw pointers very well in every method to increase performance, but in a non-intentive program, the stability of smart pointers makes them a very useful tool.
If you are not transferring ownership, unique_ptr should have the same performance than raw pointers when optimizations are enabled.
@@elcugo I guess, but why do things the way way when I could do them my way? 😉
What is the difference between `std::shared_ptr` and pass by reference `&`.
From my understanding and also from your word shared pointer is a unique pointer that allows multiple other pointers to point to its data on the heap. i.e many pointers with one data.
On the other hand pass by reference is a pointer that points to another pointer that hold the data on the heap. If the pointer that owns that data got cleaned up when it goes out of scope the reference & will point to invaild data. And in c++ the compiler will give error if doing incorrectly.
I think the thing you should have gone over more is how encapsulating them in a class like this allows them to be deconstructed automatically when they go out of scope. I find that when I write C++ I tend to not allocate anything myself and instead I just allow the container classes to handle all of that for me. If I need a custom object I can write the allocation in the constructor and deallocation in the destructor and I'm done. Since the standard container classes call my destructors for me, I don't have to care about allocation most of the time. It's only when I write C that I have to care and I've got methods for dealing with it there, but it's never as easy when I have to create some new "class" to do things with. I don't hate the way I have my data structures library laid out, but I don't love it either and I constantly wish I had operator overloading and real classes like in C++. I don't totally hate C++ and its syntax, just *most* of it.
I doubt code at 0:45 will even run to the point where it uses dangling pointer, because it allocates auth as size of auth, which is size of pointer, and it's clear from the code that auth is some struct that should be atleast 32 bytes long.
That use after free at the beginning isn't the only issue. There's quite a few issues here from basic pointer arithmetic just being wrong and duplicating strings that are not freed. Not to mention how dangerous it is to deal with C strings this way. The line buffer being 128 bytes is the only thing saving you from bad things happening with your already incorrect pointer arithmetic.
i noticed auth = malloc(sizeof auth) isntead of sizeof *auth almost immediatly
Plus, the `malloc` call being supplied a `sizeof(some_pointer_variable)` as its `size` parameter, causing it to allocate 8 bytes regardless of how large the `auth` struct actually is...
@@Uerdue I mean you could get around that by using the struct type name but maybe in this case the struct name is also auth, the full code isn't given so we can't be sure -_-
@@jlewwis1995 I can't think of any way to both typedef a struct and declare a variable of the same name in C. That's just gonna lead to compiler errors. The sizeof usage definitely appears wrong. It probably functions okay because malloc will probably allocate more than the 4 or 8 bytes (pointer size) due to it allocating pages.
I would love to see a video about Arenas. Since they are an allocation strategy that isn't used or talked about often even though it's quite powerful and solves the problem of ownership in a quite unique way.
If the function isn't intended to own the memory use std::unique_ptr& / std::shared_ptr& / T&. Better performance as well.
just pass the value by reference, that's the normal sane way
@n00blamer i.e. `T&`.
additionally, the most obvious error with the code was (unless imported by a header file which was not shown in the code) auth & service would be 2 undefined identifiers. i also think that (although i may be wrong) as im unsure of the return type of std::move() u could just define the function to take in a reference to a std::unique_ptr. that is, std::unique_ptr& as apposed to std::unique_ptr
he could, but the idea of std::move() is that it casts the parameter into rvalue reference, which matches to && first.. it's just type system hack to select the correct signature; it doesn't "move" anything it just encourages movement.. LOL.. the correct thing to do in the video's example would just pass by reference or const reference, depending on the intent.. you would only pass shared_ptr by value when you want to co-own it, let's say you have a thread and want the object to remain alive as long as the thread is alive you would capture shared_ptr, then it doesn't matter which owner dies in which order as the last one will turn off the lights. It would been more useful video if it actually illustrated useful use cases for these smart pointer types.
3:35 so passing a raw Dog pointer is risky?
not particularly, unless someone decides to be dumbass and write free(dog).. just pass by reference when the intent is just to do crap with the instance
As a fellow Rustacean this genuinely makes me bad for C++ people. You need so much boilerplate to make a unique pointer and even after all of that the compiler can't catch that it was moved and you still get a segfault
This video makes it seem harder and more error prone. You should check other videos to see that it is not that hard and it is pretty safe if you know what you are doing. I know c/c++ can have memory leak issues but unique_ptr actualy solves most of the problems
When a unique pointer is moved it becomes a null pointer( it stops pointing to the original memory but to the address 0x0000). Additionaly 99% of times you wouldn't move a unique_pointer but pass it by reference
he is not using the pointers very properly...
Meanwhile Rust has different boilerplate like lifetimes
Agree with the comments here stating that's not how unique or shared pointers should be used. I additionally found it a bit sad that it was implied that adding const to the speak function is annoying. It is (because c++ gets the defaults wrong) but it is necessary. Const correctness prevents bugs as it allows us to clearly state when a mutable pointer or ref is needed and when it is not.
This. Const correctness is vastly overlooked
It also prevents angry users who get errors out of nowhere (they would expect that a function which doesn't mutate anything would accept their const pointer, but no, the compiler just craps out).
Unique pointers are unique owner, not a the only possible pointer to an object. Passing things by reference/pointer should be the default when a function don't need to deal with ownership.
I think for function arguments (in non null situations) references are better
yeah, should've gone with a reference
also, const-ing methods and parameter types is actually really good practice, self documenting code and whatnot
in C++ we use the following idiom: void doSomething(const std::string& some_string) to declare some_string as a read-only reference and avoid copying. This is really good practice for performance and readability (yes, const& is very readable after C++ damages your brain sufficiently :)
Said "really good practice" is *REALLY GOOD PRACTICE* , it can be a reason others cuss you out for! Simply put, people often omit that word willy-nilly, which can cause significant errors if even one user of your code uses it.
Raw pointers are essential tools for performance programming. Complexity is just as bad for code as carelessness. Practice using dangerous things so you don't screw up when you need to use them. C and C++ memory management continues to exist for a lot of important reasons.
0:40: I honestly thought you meant that you were applying the sizeof operator to the auth pointer, rather than the *auth object (twice). Therefore, you only allocate 4 or 8 bytes with the malloc, and you also only clear that much with the memset, but then access much more with the strcpy() later. Which is a classic buffer overrun, and source of crashes later. Actually, if you use musl, it will crash at the free() at the latest.
BTW: Rather than malloc and memset, just use calloc(), which will return zeroed-out memory. And it may be faster, because calloc() may know that the memory is already zeroed-out.
Can you see a bug? *Me seeing tons of bugs there* LOL
But the do_something_with_the_dog, isn't that pass by value? Or is it by reference? In C you'd have to pass a double ptr for pass by reference in order to dealloc. You can modify the members, but the free would do nothing. I just forget the different rules in c++ so maybe I am wrong. I get the point though, but was curious if I was wrong
It's usually a pass by copy but unique_ptr can't be copied only moved. That's why std::move is required to pass it, it casts the value into an x value expression (somewhat similar to r value expressions in C but with a few differences), allowing the copy to instead call the move constructor. In general move should only be used when you want to transfer ownership over stuff. In general it's less expensive than a copy but can be more expensive than just using a reference. If in C++ you want to signify something is a reference you can either use pointers like in C (T* param) if you want it to be a nullable reference or use the & symbol instead to say it is a non nullable reference (T& param). Non nullable references can be used without having to dereference the parameters while nullable references (pointers) need to be dereferenced (arrow, *, etc.) (And ofc they should always be null checked in the function since they imply you can pass a nullptr into that function without issues. Pass by reference means it isn't null checked since you can't pass null by reference on accident)
@@sinom but I’m talking about the original function when it’s just a pointer and the last line *delete* is called. It wasn’t compiled or ran so I’m just not sure that would work. Again, totally get the point that the video is getting at, but, like you also explained, it would just send a copy which wouldn’t cause a bug.
@@sinom code that uses std::move can be written without it and needs to be refactored
passing unique_ptr by value and returning the same object in the function is the stupidest thing I've ever seen in my life, this video was biased and showed that the language allows this but it would be solved by passing the object itself as a reference or const reference
no c++ programmer who knows smart pointers would do what he did
You can dealloc the memory. You just can't NULL the pointer that the caller has unless you double indirect.
@@ezekieloruven ah yes. That’s right.
The big thing that I also thing is missed in the advantage of RAII in C++ and smart pointers being a part of that is the ability to reduce cognitive complexity. You can do things like just return in an error case without having to copy paste cleanup a bunch of places or use a go to. You just don’t have to think about clean up so if an input is wrong or whatever you just return instead of having these deeply nested functions that are so common is C code.
Thanks for reminding why I love Rust
skill issue
I would have actually used a unique_ptr reference in the case you presented for the unique pointer. I also kinda like the "returning the unique pointer back" solution in lots of cases
I'll allow passing shared_ptr by reference when smuggling it to it's final resting place somewhere inside the dark abyss of code that probably could've been written in more sane way to begin with, so when I wrote "I'll allow" I meant to say tolerate just barely.
In the context of what your doing, you shared an object for something small, i'd be fine in that case with passing the raw ptr from the get() method in for that, i treat raw ptrs in C++ as borrowed, not owned so that ptr passed into `do_something_with_the_dog()` would be raw if this was my code.
As a rust user my first thought would just be to pass a mutable reference into the function lol, it's so much easier, you don't even need a smart pointer to do that
men if rust is so damn good what are you doing here ? go and code in it
@@Phantom-lr6cs rust is losing it's charm as people gain xp writing it, the traction and lure of new and shiny is wearing thin.. heck, I'd rather write Swift or C# than Rust :_D
In the first example, "auth" is only freed if the first five characters of "line" are "reset". "auth" will be used again only if the first five characters of "line" are "login". Aside from the bug of using "sizeof(auth)" instead of "sizeof(*auth)" in the malloc and the subsequent memset, there is no problem with use after free.
1:56 besides the fact that you don't actually deallocate the dog, but then again, when the program exists the OS does it for you anyway
Shared pointers are great, but you've definitely gotta use them judiciously. They come with overhead, and it's easy to leave one in a place where it will never get cleaned up if you're not careful.
They come with overhead when a shared pointer gets created or destroyed
@@MI08SK or passed by value; the reference count must be thread-safe so there is either mutex or more likely some tricky atomic compare-exchange or linked-load/store contraption that is cheap most of the time except when multiple threads hit the bus lock at the same time, then caches will be busy synchronizing.. more cores.. less speed..
If a function is statically typed to Dog (or any sort of pointer to it), then it might as well be a method of Dog. That's besides the point though - your examples still make perfect sense for generic functions or functions typed to interfaces, without being cluttered.
Inicidentally I was preparing myself to rant about Yubico yesterday, but I was pleasantly surprised by how easy it was to uninstall their "code container". To their credit, they did that right.
Does .get() defeat the whole point of smart pointer. No it does not!! The whole video seems to be based on the premise that raw pointer bad. No raw pointer is a perfectly fine model of a non owning reference. Forbid junior to use new and delete, and you are golden.
Rust developers: "Look what they need just to mimic a fraction of our power."
unique_ptr came to the c++ STL before rust was even introduced. And may I ask, what do you think is so burdensome about it? Having to type 7 extra characters (as opposed to Box)? Otherwise the functionality is identical, readily available in the standard library, and even with extra features (that might not be present in rust or are behind its unsafe firewall).
Regarding your ‘mimicking’, I think you might have gotten it the other way around.
@@mariansalam @mariansalam The quote's a meme, don't take it too literally.
Though if you want to know why I personally prefer rust's approach, it is because it also gives compile-time checks for ownerships errors. This catches things like what is seen in the video at 4:30
@@mariansalam Rust does not let you move the ownership of data or unique smart pointer inside a function while allowing usage of that data after the function has been called without compiler errors. That is the power C++ can only dream of, not the extra characters you don't need to type.
couldn't you declare the Dog parameter as reference& or unique_ptr doesn't allow conversion to reference variable?
True! 👍👍
People should be more careful while using pointers. It's not a disadvantage. Pointers provide low-level controls, so it's a feature. I'm talking about pure C. A bit of proper attention eliminates the majority of memory-related bugs in C.
Ah yes, the make no mistakes strategy of programming, why nobody thought of this.
@elcugo it's why your operating systems and drivers work.
Good habits work. However they require discipline and professionalism. Most managers have too much heart to fire the guys who have neither. Once again the fish rots from the head. Engineering is not easy. It is in fact detail oriented and hard. The first real computers were designed programmed and maintained by mechanical engineers and machinists... if they didn't hit their numbers on their lathe then the gun director track solution was wrong and that kamikaze killed you and everyone who screwed up the "program". They were properly motivated and their guns usually didn't miss if the radar was online and working properly. The make no mistakes approach works just fine for real engineers that build serious tech.
@@robmorgan1214 That's a lot of nonsense. People have been killed by engineering mistakes multiple times, millions of dollars lost for memory bugs. Bridges have fallen, rockets exploded.
"Real engineers" build tolerances into their processes, because they know mistakes will happen. Important products gets tested by months before they get released.
You are stupidly and dangerously wrong.
@@robmorgan1214everyone makes mistakes, we're not machines
"Bork bork I am segmentation fault, mans best friend!"
5:30 that would've been a perfict time to introduce auto return type deduction...
as i understand things from some other videos and sources, you basically use raw and smart pointers, and there are right situations for all of them.
0:33 yes yes I was... I treat any free/delete preceding the usage of the freed/deleted object as a use after free bug weather or not it is checked for... the correct way to write that is have all of the other if statements return/continue then if I reach the bottom of the function/loop I assume that they wanted a reset ... this also fixed the issue of not handling malformed inputs which is another bug in the code (albeit a non critical 1)... actually depending on what the input is used for and how it is gathered I may not omit reset's if statement and out put an error on malformed input...
also for the sake of readability everything in that while loop should be in its own function (named something like parseServiceCommandFromInuput [I too suck at function naming it is ligit one of the hardest things in programming]) if I were trying to understand what your app does I don't necessarily care what commands you're parsing for there for it would take less time for me to realize that is what you're doing if you just tell me that is what you're doing... also having common patterns like that in [well named] functions helps with debugging because if you follow the "functions only do one thing. And that one thing is only done in that function" rule that limits the number of places I would have to look for bugs...
Bro, if you use constexpr new, then forgetting about delete will be a compiler error
0:40 you can also leak memory with multiple ‹auth› in a row
0:27 The bigger bug I see is that #includes for , and , and declarations for auth and service are missing, so the code wouldn't even compile to have a bug...
1:58 One would never try to do that in C; if you have ownership of the pointer, you must either pass it on or destroy it; if you don't, you do not kill it under any circumstances. Simple as that.
C++ 's smart pointers are optionally smart. I'm glad I switched to Rust.
should you use std::shared_ptr() or std::make_shared() btw. I dont think theres actually a difference, but still...
std::shared_ptr state is allocated in the heap, just like your "value" pointer.. make_shared does combine the allocations
And unique_ptr was designed to have zero overhead when compared with raw pointers, so really worth using it
except for passing values, for that it's just dim
The whole point of the unique_ptr is so that it makes memory management easier as compared to raw pointers. So the pointer getting freed after a functional stack gets deleted (as the deconstructor of the unique_ptr is called) is not an inconvenience, but it's instead a feature because unlike traditional pointers in heap that may remain in the memory even after going out of scope, unique_ptr make sure this doesn't happen. But i understand the problem which is what if i want to save it from getting deleted in a function so that I can then use it again after the functional stack is deleted.
I feel like examples against raw pointers are a bit synthetic.
The first example is just bad code, don’t use sequential ifs when you mean to do a choice from multiple options (switch or else if chain).
The second example is just bad practice as well. Don’t delete an object that you got as a pointer parameter. If you have to use a raw pointer, just let the object die with its scope.
If you know in advance you’ll need to transfer ownership, then purposefully use a smart pointer.
No need to bloat up the code just because someone else might be coding dangerously.
You also didn't initialize auth or service in the first loop iteration.
0:35 that's not a 'use after free' (because you're malloc'ing auth just before that) - it's a memory leak though.
The function should have taken a reference, possibly a weak_ptr if it needed to keep a long standing reference but it definitely shouldnt be taking a full on shared_ptr since it should NEVER take ownership proper just look into the pointer
Why didn't u use a weak pointer for that function situation? Its what they r made to do.
It's less overhead and more clear who owns what
What window manager do you use?
Why don't we just have our function accept a reference and then we pass *dog, where dog is a unique pointer?
is this similar to rust's Box and Rc but in C++ ?
Yes but you cannot mutate data owned by the Rc references if ownership of the data is shared across multiple different Rc references.
To add to the fun, 'this' in class methods is a raw pointer, so both speak and setName could "delete this;".
Anyone gonna mention you can pass unique_ptr by reference? Unless you need raw pointer behavior too, with unique_ptr you should prefer passing a unique pointer by reference, if you need nullability otherwise use raw pointers, else use references.
Just use 128 bit CPUs and allocate the first 64 bits for boundary checking.
I did not work with Rust but hearing about unique_ptr sounds a lot like the ownership model of Rust. But you don't seem to like it?
both "==" and "->" we saw in the beginning were dis-gus-ting. thanks for coming to my ted talk.
Which distro do you use with i3 ?
so does this mean that if you plan on passing a unique_ptr to a function, you should just make it shared?
Just pass a const reference
Just pass a const T&
No. This video really isn't doing a lot of stuff correctly. If you want to pass a unique_ptr to a function you need to ask yourself. Do you want that function to then own that unique_ptr or does it not need to own it at all. Ownership is usually only relevant if e.g. you're then gonna add it to a vector, member of some struct etc. if like this you just want to call a function on that object in the unique_ptr then what you actually want to do is pass it by reference instead of value. so instead of using
function(unique_ptr ptr)
{
ptr->do_something();
}
you want to do
function(unique_ptr& ptr)
{
ptr->do_something();
}
(preferably even const ref if possible)
No need for shared_ptr for simple stuff like this
shared_ptr is only necessary when for example you have two different vectors that both need to have objects they share between them
Well, raw pointers are not that bad as long as you know what is actually going on in your code. Make the program too complicated or allow random people to mess with it without knowing what they do - anything will fail. I think maybe C++ is not perfect for certain tasks. Maybe, in some scenarios - you just can't avoid it being unsafe. And every effort to mitigate it makes it more complex, and the more complex it is, the probability of error increases. Now the case where "unsafe" is "safe": a block of code. Relatively small, readable, doing one thing, invoked in a few exactly known places. Made to be as efficient as it gets. If you test all the edge cases (because it's possible for certain scenarios) - it is SAFE. And probably worth doing. When you go into more variable territory - we're getting less and less sure if the new added complexity REALLY pays off.
But why doing soemething like you did:
do_something_with_the_dog(ralf);
instead just this:
ralf->do_something_with_the_dog();
???
You could remain unique_ptr and still have void and not need to pass dog to the dog's class
Elegant solutions or syntax sugar?
Why can't you just pass a reference to the unique ptr? Wouldn't that retain ownership in the caller?
Do you only teach C in the “academy”?
Interested in more advanced topics in C++, not so much in C.
Let me know if you got something to suggest :D
I hate the behaviour of adding values to pointers in C... why does adding one add the size of the pointer's data type? I hate having to cast to uintptr_t every time I do pointer math.
Because that's way more logical than what you want... also, if you really want to misalign the pointer, you should be casting to (char *) (or its signed or unsigned variants) instead of (uintptr_t); the latter is for getting the address out of a pointer.
Did you think we wouldn't notice the camera getting mirrored?
You should almost never directly call new or delete if you use smart pointers.
With that rule passing in a raw pointer is fine if you want to do a null check, but that function should have taken a Dog reference.
Using shared pointer is a horrible idea unless you actually need the functionality it provides. The performance overhead of reference counting is huge.
Passing smart pointers between functions should be only done as a sign of explicit ownership transfer.
Is it REALLY that huge?
I always use raw ppinters, not smart pointers and my code never crashes. If you write correct code without memory leaks, raw pointers are fine to use and much less complicated than smart ones.
You forgot the pass for reference. Passing for reference the smart pointer to the function do_something_with_the_dog work and let the responsability of the resource ralf in the main scope. Nice work and nice effort. Keep pushing!
so it's basically rust without the cool compiler hints but instead gibberish errors that you have to guess
Bad example. Should have just passed Dog as a reference like Dog & d or const Dog & d. A function that operate on an object should not care about if it is a pointer or not. When you call the func, just dereference it.
Also should not pass shared pointer to a function like that, the function does not own anything. Doing this also incur unnecessary performance overhead from the atomic reference counting. Okay if you do it less frequently, but I would never do this in any function that will be called hundreds of times or more.
Call me dumb, bt I fell like practicing some good pointer habits is much better than using smrt pointer. Such complex syntax alone can cause serious bug. After all, you gotto manually check and ensure more stuff with smart pointer than regular raw pointer.
Why not use a const unique_ptr&?
drop the whole unique ptr, just take in: const Dog&, and call it: foo(*dog) ; const is optional, depends on the intent
0:33
you know, to call something "vulnerability" you need to make sure it really is something serious and not just a clear sign of 1iq brain
I still prefer Rust's way of memory safety, but this is definitely useful for when I'm inevitably snooping around c++ code.
Unique pointers _are_ Rust's way (ownership and borrow semantics) but C++ just defaults to being unsafe for compatibility.
Likewise, shared pointers are the same as using Rc in Rust.
I definitely prefer the Rust syntax though it's way less verbose!
Rusts way includes compiler errors on such mistakes@@seabrookmx
@@seabrookmx The "Rust's way" happens at compile time without overhead during runtime unlike with C++ if you use smart pointers whose references will be counter during runtime. If you opt for speed by not using smart pointers with C++, you'll pay for it in terms of potential runtime crashes. If you explicitly use Rc in Rust, then you are on the same line with C++'s smart pointers in terms of speed. In Rust you still pay for the complexity of dealing with the borrow checker, which may or may not be a problem dependening on the developer.
Wow this is like the nightmare version of Rust and still segfaults if you don't do all that voodoo.
Bro, this should be intro for Rust
lolz kids : D why are you bunch of rust clowns here doing ? go and code in that crap : D
you think this is exists in c++ cause of rust shit ? in c++ smart pointers were there before rust shit appeared : D go and cry for your rust no one cares :D cuz its dumb and shit syntax and crap langauge where you need 15 mb for just hello world and compilation takes forever : D
I don't understand why can't you just have a unique_ptr& argument and make the first unique_ptr problem go away?
Or you just always set unallocated pointers to null and always check for null before using and set to null after freeing it. Also be careful when reassigning it not to leave dangling unused memory.
check if the value is null or not, if its not then pass it by references