CppCon 2016: Herb Sutter “Leak-Freedom in C++... By Default.”

Поделиться
HTML-код
  • Опубликовано: 26 дек 2024

Комментарии •

  • @gavinchou3022
    @gavinchou3022 5 лет назад +32

    This talk shows the best practice of using smart ptr:
    * share between objects, use shared ptr
    * scoped and stack-like, use unique ptr
    * circular reference, use weak ptr, and this should be avoided when design
    * no ownership relation, use raw ptr
    and the second half of this talk shows us an interesting GC like mechanism.

  • @jvsnyc
    @jvsnyc 3 года назад +10

    This is painfully good. I have watched several excellent videos on this general topic, which deserve to be called excellent, but this surpasses all of them. I used to feel a lot of his presentations went over my head, either I got smarter, he got even better at teaching, or it is true and Modern C++ is getting easier. There is still a huge amount to know if you insist on knowing all of the old, weird, hard ways of doing things as well as all the newer, easier ways provided now. Even just learning the modern best ways is a fair amount, but either way, what a great talk.

  • @nelsondavenapalli8802
    @nelsondavenapalli8802 8 лет назад +30

    Excellent talk. C++ is getting so much interesting than a decade back :D

  • @anfield6321
    @anfield6321 8 лет назад +9

    I don't know why but I simply enjoy listening to him lol.

  • @RobertFisher1969
    @RobertFisher1969 8 лет назад +6

    Great to see this! I bought a book on garbage collection back in the late 1990s to try to implement something like deferred heap. (I think I ended up trying for RC with a mark-sweep to catch the cycles. Mentioning being in bed with the allocator in particular brought back vivid memories of trying to overload new to track internal pointers.) I eventually convinced myself that it wasn’t quite possible in a safe way in C++ at the time. Though it may have been that I just didn’t know enough or didn’t constrain the problem enough.

  • @ruslany28781
    @ruslany28781 8 лет назад +8

    Excellent talk! Many insights on using smart pointer efficiently. Oh my, after 1:00:00 it is so exciting and cool stuff!
    Also, a guy remarked to be cautions when using make_shared with weak_ptr, because object's memory will still be allocated even when the object is destroyed. Nice catch!

  • @DedmenMiller
    @DedmenMiller 8 лет назад +19

    This is the first cppcon talk where I'm thinking "oh crap.. i did that wrong yesterday" Had to pause the video to put a fix for that onto my todo list.

    • @kksrinivas24
      @kksrinivas24 5 лет назад +3

      Same here. Spent a whole day creating a Tree only to realize at midnight after watching the video that it leaks. Have to fix it now

  • @hansolo7988
    @hansolo7988 8 лет назад +61

    My favorite Microsoft guy ;)

    • @almukmin7738
      @almukmin7738 8 лет назад +10

      Han Solo Microsoft is lucky to have him

  • @renedes6682
    @renedes6682 7 лет назад +5

    Super talk, slowly dragging myself away from my C++98 past.

    • @higgins007
      @higgins007 5 лет назад +3

      Me too, but the process has convinced me to never ever use c++ again unless it's absolutely necessary for some reason outside my control.

  • @jvsnyc
    @jvsnyc 3 года назад

    ~40:00 or so gets to places weak_ptr is actually necessary. Others use them where raw pointers would have done just fine, or hand-wave away by saying "not needed very often". This shows where you DO need them.

  • @micaevski
    @micaevski 8 лет назад +2

    i was skeptic going into this talk because it seemed old news. but herb nails it. super recommended.

  • @daggawagga
    @daggawagga 8 лет назад +1

    The callback logic / trick with weak_ptr is so sweet.
    I feel silly for not realizing it by myself.

  • @prasadjoshi124
    @prasadjoshi124 7 лет назад +4

    Awesome talk. He talks about recursive stack destruction ((@17:03) caused by unique_ptrs. One way to avoid it is delete leaf nodes manually. However, then what is the point of using unique_ptr for tree if in destructor we delete all the nodes manually? Why not use simple pointers, we are deleting them anyway?

  • @Shockszzbyyous
    @Shockszzbyyous 7 лет назад

    i learned this a while back, it's been awesome ever since.

  • @TheMrPippo
    @TheMrPippo 4 года назад

    15:06 I believe that a bigger problem with the reference to the parent instead of the pointer is that the root does not have a parent and a reference cannot be nullptr.

  • @origamibulldoser1618
    @origamibulldoser1618 8 лет назад +18

    I can't think of a snarky comment.

  • @mikevasiljevs412
    @mikevasiljevs412 8 лет назад +2

    Can please somebody explain the joke with the rabbit?

  • @Scorp1u5
    @Scorp1u5 7 лет назад +4

    Can someone explain the "auto wahlberg = [this]" code?

    • @jeremydavis3631
      @jeremydavis3631 4 года назад +4

      It's a few years later, but here I go.
      The lambda that begins on that line implements a mark-sweep traversal of the graph. It's called wahlberg as a reference to the actor Mark Wahlberg, whose first name is also the first word in the name of the algorithm.

  • @MikhailMatrosov
    @MikhailMatrosov 8 лет назад +4

    I am surprized that you really recommend to use unique_ptr for fixed-size dynamic arrays. Because a) you need another field to store its length, which breaks encapsulation, and b) you don't have pretty STL interface to this array, which is not convenient. You cannot even use range-based for on it without some wrappers. Are you sure this is the recommendation you want to give in a video that one could share the link to?

    • @kwanarchive
      @kwanarchive 8 лет назад

      I don't see the problem. Your type which uses the unique_ptry could itself declare begin and end functions which return T[]+i, and it will automatically be usable with range for. What's even better is that pointers are already traited as random access iterator and you know that it's a pointer and not a wrapped iterator class. AND you know it really can't be resized.
      It's a really lightweight but still clean solution.

    • @MikhailMatrosov
      @MikhailMatrosov 8 лет назад +1

      If I declare begin() and end() functions in my type, this means that I can possibly have only one such array in my class. Suitable in some situations, but definitely not in all situations. It can't be resized, it is true, and this one is convenient. However, my concern is that this pro does not outweighs all the cons in most situations. This is really a light-weight solution, agree. However, vector is only two pointers bigger, that's all. If you need to have millions of such arrays, ok, it might be meaningful. However, in most situation you just don't care about two extra pointers.

    • @kwanarchive
      @kwanarchive 8 лет назад +4

      You can easily create a dedicated wrapper type for unique_ptr and use multiple of those wrapper types in your actual class. That way, each of them will individually be usable in begin/end scenarios.
      One thing unique_ptr has over vector is that you can release your pointer to no longer be managed by it. With vector, you can't do that. You would have to move each individual object out of a vector first. Some C libraries actually require that ownership of an allocated array be given to it to manage.
      You can also supply a custom deleter, which you can't do with vector.
      Furthermore, you can do unique_ptr. Vectors can't do contiguous multidimensional arrays.

    • @MikhailMatrosov
      @MikhailMatrosov 8 лет назад +1

      Ok, these are some additional benefits, and they may come up handy in appropriate situations, like you described. However, to make this truly usable, as you've said, you need to create another wrapper for unqiue_ptr. And as Herb said, this is a video you can share a link to, i.e. it is mostly valuable for newbie developers. And for newbie developers I doubt that "make you own wrapper around (stuff)" is a good advice to start. At least in my domain I am 99% of the time perfectly happy with vector even when I don't need to change its size, and I think this should be the default advice.

    • @kwanarchive
      @kwanarchive 8 лет назад +3

      "Share a link to" doesn't mean this is for newbies. But I don't think creating wrapper classes is out of the domain of a newbie.
      Herb goes really in depth about cycles and stack blowouts, which are not newbie stuff. Newbie stuff I don't think would even want to discuss how you'd go about implementing a doubly linked list - we have std::list for that and that's what newbies should use.
      This video is for people who are either learning about the lower level or are either in, or wanting to be in, the business of writing libraries for off-the-shelf use. In that sense, this video is for newbies to writing resource management libraries for complex data structures.
      Every video on CppCon you can share a link to. This is just one where it's the most applicable to the broadest range of programmers, because efficient resource management is applicable everywhere in C++.

  • @kuhluhOG
    @kuhluhOG 4 года назад +2

    39:50 wait, Java and C# have this problem too?
    I thought the job of the garbage collector is to stop that from happening?

    • @darkengine5931
      @darkengine5931 4 года назад +2

      It's extremely easy to leak even in GC languages that handle cycles. For example, say we have an Image object and it's huge -- megabytes worth of data. Then in a shader, we store:
      // (not C++ code):
      class Shader
      {
      public Shader(Scene scene) {img = scene.some_img;}
      private Image img;
      }
      Suppose the user requests to remove the image from the scene, but we forget to null the reference in the shader. Voila, the image gets leaked, and its memory won't be freed until the shader itself is garbage collected (possibly not until the entire software is shut down). A solution to help protect against such mistakes is to use weak references here but people tend to not use them nearly enough. If we make this same type of mistake in a language like C, you get a dangling pointer instead of memory leak since we'd call free on the image. I actually find that preferable to the logical leak since it tends to hard crash and is easy to detect in tests. Logical leaks are pretty hard to track down.
      But there's also just a practical observation. What are the leakiest applications out there? Some off the top of my head are lots of Flash games. Flash uses garbage collection (proper GC that handles cycles). It's certainly not professionally-developed C applications that leak left and right. But there are a whole lot of Flash games floating around on the net that use more and more memory and get slower and slower the longer they run. In fairness, a lot of people programming these Flash games are just amateur developers and likely not very disciplined, but GC is hardly a silver bullet against memory leaks.
      What GC protects you against are physical memory leaks (though these are the easiest to detect and fix, and destructors do just as good of a job eliminating those), not logical ones, and it protects against dangling pointer bugs. It offers no protection against logical leaks and actually makes them easier to create in many cases.

  • @amir_tv1
    @amir_tv1 7 лет назад +2

    Not sure why he makes it seem that you can just define a node struct with smart child pointers and everything works out fine. I can't find any decent material covering how to actually use smart ptrs to implement a data structure... particularly a tree

  • @think2086
    @think2086 4 года назад

    Is the code on the right hand side correct @18:43? I can't make sense of it. At first I thought he was using some strange idiom of subscript[0] on a pointer to mean "dereference," but that doesn't work, so now I think he just plumb forgot to dereference variable 'leaf' and I think it should be like this:
    while(n->children.size() > 0)
    {
    auto leaf = &n->children;
    while((*leaf)[0]->children.size() >0)
    leaf = &((*leaf)[0].children);
    leaf->pop_front();
    }
    but that's arguably quite ugly and would look better as:
    while(n->children.size() > 0)
    {
    auto leaf = &n->children;
    while(leaf->front()->children.size() >0)
    leaf = &leaf->front().children;
    leaf->pop_front();
    }

  • @Jon2029
    @Jon2029 8 лет назад

    Great stuff. Two questions, though. On the slide at 54:43, vector::erase is called O(N) times. Won't this result in O(N^2) runtime due to the member copies?
    Also, the slide at 42:46 says that a unique_ptr and unique_ptr& members has equal space to correctly written manual code. I assume the manual code would just have a single raw pointer. How are these solutions equal space?

    • @IsmeGenius
      @IsmeGenius 8 лет назад

      1. Yes it will be O(N^2). It is pseudocode, though.
      2. It does not say space would be equal.

  • @AxelStrem
    @AxelStrem 7 лет назад

    @25:10 Herb says that recursive destruction is the worst for a list - but since it's a recursion of width 1, wouldn't the compiler be able to optimize it away as a tail recursion? or better, wouldn't it be better is the standart guaranteed it to be optimized away in such cases (as it does for copy elision now)?

    • @HebaruSan
      @HebaruSan 7 лет назад +1

      I could be wrong, but I thought that the destructors of my class's data members had to be called before my class's destructor, not after. So if I call delete on the List object, it would first have to destroy all the nodes in depth-first order. Tail recursion only works if the work for the current node can be completed before the recursive call, not after.

    • @AxelStrem
      @AxelStrem 7 лет назад

      HebaruSan you're absolutely right, from the C++ point of view it's very important to guarantee the nested lifetime. Hovewer the compiler is free to rearrange a lot of things, including memory accesses and other side effects sometimes, so I was wondering what can be done in this case. I'll have to agree that probably not much:)

  • @metashifter
    @metashifter 8 лет назад +1

    9:17 paused precisely, *WIZARD GESTURE*

  • @Niksan1974
    @Niksan1974 7 лет назад

    Anyone know which talk of Dan Saks, Herb Was referring to?

  • @konstantingeist3587
    @konstantingeist3587 6 лет назад

    "shared_ptr is as fast as you can do it by hand" Not really though. They allocate additional memory to track reference counts. If you did it by hand, you could embed reference count directly inside the object without additional memory allocations.

    • @yonilavi1363
      @yonilavi1363 6 лет назад +4

      make_shared puts the control block together with the object itself, which is just as good. And you also get weak_ptr support, which you can't have with an intrusive smart pointer (as when the object is destructed, your weak ref will become dangling).

  • @Yupppi
    @Yupppi Год назад

    "In C++ there is no garbage to collect!"

  • @childhood1888
    @childhood1888 2 года назад

    58:49 Darn it, that was my question!

  • @maggotroot
    @maggotroot 3 года назад +2

    looks like smart pointers add more unreadable code than fix problems. Especially in the tree delete example

  • @mina86
    @mina86 4 года назад

    42:40 - that’s a *terrible* implementation. How do you remove the first element from the list? Not to mention that it wastes space with every node for a pointer which is needed in the last element only.

  • @IndellableHatesHandles
    @IndellableHatesHandles 2 года назад

    I have no clue what is going on. I think I'll stick to C#.

  • @zloidooraque0
    @zloidooraque0 7 лет назад

    that's brilliant stuff.

  • @Fetrovsky
    @Fetrovsky 8 лет назад

    Posted the poster.

  • @childhood1888
    @childhood1888 2 года назад

    29:22

  • @lockbert99
    @lockbert99 7 лет назад

    He sure is a hand waver. Seems more like exercise or fidgeting than accentuating the talk.

  • @joe-ti8rz
    @joe-ti8rz 7 лет назад

    Metaphysics

  • @paulfunigga
    @paulfunigga 8 лет назад +1

    Are these people gonna add multidimensional std::array and BigNumbers into C++, ever?

    • @MushyMellowMenu
      @MushyMellowMenu 8 лет назад +3

      What do you mean by multi-dimensional std:array? std:array is just a wrapper for a normal array. Arrays don't have multiple dimensions either. They just represent a contiguous region of memory, which is used to store multiple variables of the same type. If you want "multiple dimensions" you can either make an array of arrays (for example: int array_2d[5][5]; ), or you can index a normal array by row and column ( some_array[width*row+column] ). The same can be done for std:array, though the syntax is a little different for std:array of std:array ( std::array array_2d; )
      As for big numbers: For something to be added to the C++ standard library, someone has to make a proposal, which has to go through multiple rounds of feedback and improvement until it might be added to the standard. If you think, that some feature would improve C++, you can do that work, but please don't complain, while not lifting a finger yourself. There might also be legitimate reasons for not adding arbitrary size/precision numbers to C++, like different requirements which can't be combined in a single C++ standard library feature.
      Edit:
      If you are looking for an existing arbitrary size number implementation, you could use the GNU Multiple Precision Arithmetic Library (GMP).

    • @paulfunigga
      @paulfunigga 8 лет назад +1

      I don't like writing std::vector _3d(std::vector(std::vector(n1), n2),n3) (might have fucked it up without a compiler) when I want a n1 by n2 by n3 3d array. Why can't I just do array? You can do this with variadic templates. I don't need to work on any features all of this stuff is already implemented in boost, I just don't understand why they won't add these basic things into C++.

    • @MushyMellowMenu
      @MushyMellowMenu 8 лет назад +2

      "I don't need to work on any features all of this stuff is already implemented in boost"
      It seems you didn't fully read my reply. The work I was talking about is the process of standardization. I don't know, who you think "they" are, but if you want some feature in the standard, you have to get others interested, write a proposal, repeatedly improve that proposal and present it to the standards committee. Here is a link to an article explaining the process: isocpp.org/std/submit-a-proposal .

    • @TheThirdAttractor
      @TheThirdAttractor 8 лет назад +1

      submit a proposal - the committee works with proposals

    • @totof2893
      @totof2893 2 года назад

      @@paulfunigga multi dimensional array is just a wrapper on a single dimension array, where you do index computation.
      It can easily be written by you when you need it, even the variadic one. It does not worth the price of standardize it.

  • @tpulkkin
    @tpulkkin 7 лет назад

    Hmm, in the beginning of his presentation he's going Data d; => unique_ptr ptr; => shared_ptr ptr; path. But that is NOT why people use smart pointers. The real path is this: Data d; => Data *ptr; => Data *ptr; // owned => shared_ptr ptr;. But the real problem is that the first step from Data d; to Data *ptr; is very evil, since it happens because Data d; cannot be compiled, since compiler gives compile-time error. This error is because struct A { Data d; }; struct Data { }; doesnt compile, since structs are in wrong order in the translation unit. This is some big problem in c++, since it depends on sizeof(Data) to be availble when struct A is being done, and even forward declaration doesn't fix the problem. The conversion from Data d; to Data *d; makes forward declaration work, and the consensus in c++ programmer community is that all pointer usage must be converted to smart pointers. Thus people's reason to use smart pointers is simply that otherwise the program does not compile. Data d; solution is currently only working in c++ for native types like floats and unsigned ints, and user-defined types all are broken in c++. This is serious problem since it prevents many nice stuff in people's programs, like references in data members are all broken, and it requires nasty "flatten hidden dependency tree" to place multiple classes in same file.
    Obviously if you do some crazy stuff like follow some conventions, it _can_ be fixed other way too, but modern programming languages shouldn't have this level problems any longer.

  • @llothar68
    @llothar68 6 лет назад

    Another reason to avoid smart pointers: They make it impossible to use implementation hiding by forward declaring "class Foo;" including the popular PIMPL pattern.
    This can be terrible for large systems compile time.

    • @ytsas45488
      @ytsas45488 5 лет назад +8

      wut? you can definitely use unique pointers with an opaque type.

  • @MrGuardianX
    @MrGuardianX 7 лет назад +8

    I fail to see how C++ become more user-friendly while the whole audience of C++ oriented programmers can't answer any questions Herb asks. Meanwhile they invented 3 more types of pointers. Jesus.

    • @HebaruSan
      @HebaruSan 7 лет назад +1

      C++ pays dearly for the ability to allow the programmer to put composite types on the stack instead of the heap.

    • @mohammedtalat6187
      @mohammedtalat6187 6 лет назад +2

      LOL, that comment cracked me

    • @butchertibi6039
      @butchertibi6039 6 лет назад

      "how many of you know about shared aliased pointers . . . more of you will know now" 27:40

  • @Dima-ht4rb
    @Dima-ht4rb 8 лет назад +2

    Im annoyed by attempts to speak with the audience, "lets do everything in one place, for future reference" but also lets spend time on listening to silence from an audience, because comedy.

    • @ryanwilson8629
      @ryanwilson8629 7 лет назад

      Dmitry Shap I'm annoyed by this as well. good info but the delivery is too slow.

  • @cartesius33
    @cartesius33 8 лет назад +5

    Oh, sorry, but this video is like a counterexample against C++(11+). Linus was (regrettably) right. When doing interviews in our company even experienced C++ programmers can't explain even some basic aspects of the current C++. C++ is a language where everything seems logical but the elegance of design has disappeared long time ago.

  • @llothar68
    @llothar68 8 лет назад +2

    I'm sorry, i think this is all a solution to a problem that isn't worth all the overhead and downfalls. Use good testing and memory tracking tools and you go leak free too.
    There are more important thing for C++ (Concepts, Contracts, Libraries). This is just a cazy idea to get Java programmer back.

    • @SaiTorrKalFas
      @SaiTorrKalFas 8 лет назад +17

      Sure, you can use "good testing and memory tracking tools". But then, you do not have leak free by definition, which is 1) the point of the talk and 2) an entirely different thing.
      As for overhead: How much overhead does std::unique_ptr have compared to new + delete?

    • @givememorebliss
      @givememorebliss 8 лет назад +6

      Being leak-free must never be tested for. It should be something that is universally true by definition, and the techniques shown here help the programmer achieve that.

    • @llothar68
      @llothar68 8 лет назад

      Sorry you don't get leak free in reality with this. You can leak memory in data structures too. It's a pretty and almost pure academic problem.
      Even in serious aviation or power plant software this is a very minor a problem compared to the processes in place.
      It's just like the other new HOT discussed topic from the theory department: null/void free programming. Seriously?

    • @SaiTorrKalFas
      @SaiTorrKalFas 8 лет назад +9

      Would you like to elaborate on the "You can leak memory in data structures too."-part?
      "compared to the processes in place": If you mean established checking and testing structures you mentioned earlier to avoid leaks with e.g. raw pointers: That's like saying "It's OK to fall out of this window all the time, because, we have this super fancy and expensive security net down there, which catches everything. Well, in almost all cases that is."

    • @bloopbleep7082
      @bloopbleep7082 4 года назад +1

      You just contradicted yourself with "not worth the overhead and downfalls." Smart ptrs *remove* potential downfalls. Any "downfall" you have with smart ptrs you'll have twice over with raw pointers. Additionally, there is very little overhead. You'd still need to do this management with raw pointers to avoid leaks, smart ptrs just make it implicit. You are the one inventing imaginary issues to push your narrative, I'm afraid.

  • @childhood1888
    @childhood1888 2 года назад +1

    26:36