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

  Рет қаралды 145,185

CppCon

CppCon

7 жыл бұрын

CppCon.org
-
Presentation Slides, PDFs, Source Code and other presenter materials are available at: github.com/cppcon/cppcon2016
-
Lifetime safety means writing code that, by construction, is guaranteed to eliminate two things: (a) use of null/dangling pointers (including pointerlike things such as references, iterators, views, and ranges), and (b) leaks (including the rare 1% case where we’re tempted to admit the possibility of an ownership cycle or need to support lock-free concurrent data structures).
Last year, my CppCon 2015 talk “Writing Good C++14… By Default” focused on (a), null/dangling, because it's the more difficult and usually more serious problem. I gave an overview of a new approach of using static analysis rules to eliminate use of null and dangling in C++. That work continues and we’re in the process of writing down the formal rules for the approach that I showed last year.
This year, the focus will be on (b), leaks: The talk aims to begin with a set of simple rules, the “5-minute talk” to demonstrate that a handful of rules can be taught broadly to programmers of all levels, and results in code that is clean and free of leak bugs by construction.
But, since we’ll still have 85 minutes left, we can use the time to spelunk through a series of “Appendix” code examples, in which we'll demonstrate "why and how" to apply those rules to a series of increasingly complex/difficult situations, and that are aimed at increasingly advanced and “clever” (note: not always a good thing) programs and programmers. We’ll address questions such as: How should we represent Pimpl types? How should we represent trees - what should the child and parent pointer types be, and (when) should they be unique and when shared? How should we deal with “intra-module” or “encapsulated” cycles when you control all the objects in the cycle, such as all the nodes within a Graph? And what about “inter-module” or “compositional” cycles when you don’t know in advance about all the objects that could be in the cycle, such as when combining libraries written by different people in a way that may or may not respect proper layering (notoriously, using callbacks can violate layering)? The answers focus on cases where we have solid guidance, and then move toward some more experimental approaches for potentially addressing the ~1% of cases that aren’t yet well covered by unique_ptr, shared_ptr, and weak_ptr.
-
Herb Sutter
Software architect, Microsoft
Author, chair of the ISO C++ committee, software architect at Microsoft.
-
Videos Filmed & Edited by Bash Films: www.BashFilms.com
*-----*
Register Now For CppCon 2022: cppcon.org/registration/
*-----*

Пікірлер: 105
@gavinchou3022
@gavinchou3022 5 жыл бұрын
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.
@nelsondavenapalli8802
@nelsondavenapalli8802 7 жыл бұрын
Excellent talk. C++ is getting so much interesting than a decade back :D
@jvsnyc
@jvsnyc 3 жыл бұрын
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.
@anfield6321
@anfield6321 7 жыл бұрын
I don't know why but I simply enjoy listening to him lol.
@DedmenMiller
@DedmenMiller 7 жыл бұрын
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 4 жыл бұрын
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 7 жыл бұрын
My favorite Microsoft guy ;)
@almukmin7738
@almukmin7738 7 жыл бұрын
Han Solo Microsoft is lucky to have him
@Shockszzbyyous
@Shockszzbyyous 7 жыл бұрын
i learned this a while back, it's been awesome ever since.
@RobertFisher1969
@RobertFisher1969 7 жыл бұрын
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.
@micaevski
@micaevski 7 жыл бұрын
i was skeptic going into this talk because it seemed old news. but herb nails it. super recommended.
@RuslanYushchenko
@RuslanYushchenko 7 жыл бұрын
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!
@daggawagga
@daggawagga 7 жыл бұрын
The callback logic / trick with weak_ptr is so sweet. I feel silly for not realizing it by myself.
@renedes6682
@renedes6682 7 жыл бұрын
Super talk, slowly dragging myself away from my C++98 past.
@higgins007
@higgins007 4 жыл бұрын
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.
@prasadjoshi124
@prasadjoshi124 7 жыл бұрын
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?
@zloidooraque0
@zloidooraque0 7 жыл бұрын
that's brilliant stuff.
@Niksan1974
@Niksan1974 6 жыл бұрын
Anyone know which talk of Dan Saks, Herb Was referring to?
@origamibulldoser1618
@origamibulldoser1618 7 жыл бұрын
I can't think of a snarky comment.
@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.
@Jon2029
@Jon2029 7 жыл бұрын
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 7 жыл бұрын
1. Yes it will be O(N^2). It is pseudocode, though. 2. It does not say space would be equal.
@mikevasiljevs412
@mikevasiljevs412 7 жыл бұрын
Can please somebody explain the joke with the rabbit?
@MikhailMatrosov
@MikhailMatrosov 7 жыл бұрын
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 7 жыл бұрын
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 7 жыл бұрын
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 7 жыл бұрын
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 7 жыл бұрын
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 7 жыл бұрын
"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++.
@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(); }
@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 жыл бұрын
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:)
@Scorp1u5
@Scorp1u5 6 жыл бұрын
Can someone explain the "auto wahlberg = [this]" code?
@jeremydavis3631
@jeremydavis3631 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.
@patrickproctor3546
@patrickproctor3546 7 жыл бұрын
Isn't the iterative subtree release algorithm O(n)? You have to traverse the tree from top to bottom again after every released leaf. Actual performance-wise, it may still be faster than a recursive deletion purely because of stack frames, but I think we'd be best off with an explicit stack of the nodes or an array/vector of references to each array/vector of children.
@mina86
@mina86 4 жыл бұрын
In degenerate case where you have tree of breadth one, the complexity is O(n²). If you have balanced tree it’s O(n log n). In degenerate case where you have tree of depth one, it’s O(n).
@hdswashere
@hdswashere 3 жыл бұрын
@@mina86 I've been searching for a comment about this. I thought the whole point was to have a destructor that works even for unbalanced trees. Quadratic complexity seems like a terrible oversight. This would be solved by using pop_back() and back() instead. I can only imagine it wasn't done like that to ensure that subtrees are cleared in a particular order.
@amirtv106
@amirtv106 6 жыл бұрын
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
@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 жыл бұрын
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).
@childhood1888
@childhood1888 2 жыл бұрын
58:49 Darn it, that was my question!
@metashifter
@metashifter 7 жыл бұрын
9:17 paused precisely, *WIZARD GESTURE*
@Fetrovsky
@Fetrovsky 7 жыл бұрын
Posted the poster.
@Yupppi
@Yupppi 5 ай бұрын
"In C++ there is no garbage to collect!"
@kuhluhOG
@kuhluhOG 4 жыл бұрын
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 3 жыл бұрын
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.
@maggotroot
@maggotroot 3 жыл бұрын
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 Жыл бұрын
I have no clue what is going on. I think I'll stick to C#.
@joe-ti8rz
@joe-ti8rz 6 жыл бұрын
Metaphysics
@childhood1888
@childhood1888 2 жыл бұрын
26:36
@lockbert99
@lockbert99 7 жыл бұрын
He sure is a hand waver. Seems more like exercise or fidgeting than accentuating the talk.
@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 жыл бұрын
wut? you can definitely use unique pointers with an opaque type.
@paulfunigga
@paulfunigga 7 жыл бұрын
Are these people gonna add multidimensional std::array and BigNumbers into C++, ever?
@MushyMellowMenu
@MushyMellowMenu 7 жыл бұрын
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 7 жыл бұрын
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 7 жыл бұрын
"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 .
@onqtam
@onqtam 7 жыл бұрын
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.
@Dima-ht4rb
@Dima-ht4rb 7 жыл бұрын
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 7 жыл бұрын
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.
@MrGuardianX
@MrGuardianX 7 жыл бұрын
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 жыл бұрын
C++ pays dearly for the ability to allow the programmer to put composite types on the stack instead of the heap.
@mohammedtalat6187
@mohammedtalat6187 6 жыл бұрын
LOL, that comment cracked me
@butchertibi6039
@butchertibi6039 5 жыл бұрын
"how many of you know about shared aliased pointers . . . more of you will know now" 27:40
@llothar68
@llothar68 7 жыл бұрын
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 7 жыл бұрын
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 7 жыл бұрын
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 7 жыл бұрын
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 7 жыл бұрын
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 3 жыл бұрын
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 жыл бұрын
29:22
CppCon 2017: Chandler Carruth “Going Nowhere Faster”
1:00:58
She’s Giving Birth in Class…?
00:21
Alan Chikin Chow
Рет қаралды 10 МЛН
ПАРАЗИТОВ МНОГО, НО ОН ОДИН!❤❤❤
01:00
Chapitosiki
Рет қаралды 2,5 МЛН
狼来了的故事你们听过吗?#天使 #小丑 #超人不会飞
00:42
超人不会飞
Рет қаралды 62 МЛН
Black Magic 🪄 by Petkit Pura Max #cat #cats
00:38
Sonyakisa8 TT
Рет қаралды 34 МЛН
CppCon 2016: Ben Deane “Using Types Effectively"
55:20
CppCon
Рет қаралды 45 М.
CppCon 2016: Jason Turner “Practical Performance Practices"
1:00:29
CppCon 2015: Sean Parent "Better Code: Data Structures"
1:04:00
SIMD Libraries in C++ - Jeff Garland - CppNow 2023
1:30:07
CppNow
Рет қаралды 10 М.
Emulating a CPU in C++ (6502)
52:28
Dave Poo
Рет қаралды 952 М.
She’s Giving Birth in Class…?
00:21
Alan Chikin Chow
Рет қаралды 10 МЛН