WEBVTT

1
00:00:00.040 --> 00:00:04.559
<v Speaker 1>Okay, let's unpack this. Imagine your software running right, doing

2
00:00:04.599 --> 00:00:09.080
<v Speaker 1>its thing, but underneath all that, there's this this hidden world,

3
00:00:09.359 --> 00:00:12.439
<v Speaker 1>unseen forces basically running the show. It's the connection between

4
00:00:12.480 --> 00:00:15.759
<v Speaker 1>your code and the computer's memory. Today we're doing a

5
00:00:15.800 --> 00:00:19.760
<v Speaker 1>deep dive into C plus plus memory management. It's fascinating stuff,

6
00:00:20.079 --> 00:00:24.160
<v Speaker 1>but yeah, often pretty intimidating too. We're using Patresea's really

7
00:00:24.160 --> 00:00:27.039
<v Speaker 1>comprehensive book as our guide here. Now. C plus plus

8
00:00:27.039 --> 00:00:29.600
<v Speaker 1>memory Management it can feel like a maze, you know,

9
00:00:30.239 --> 00:00:33.280
<v Speaker 1>full of complex rules, hidden traps. But our mission today

10
00:00:33.320 --> 00:00:35.200
<v Speaker 1>is to give you a shortcut. We want to boil

11
00:00:35.280 --> 00:00:37.799
<v Speaker 1>down the core ideas, point out those pitfalls, and look

12
00:00:37.799 --> 00:00:40.159
<v Speaker 1>at the modern ways of doing things. Hopefully help you

13
00:00:40.159 --> 00:00:42.960
<v Speaker 1>write C plus plus code that's better, safer, more expressive.

14
00:00:43.320 --> 00:00:46.240
<v Speaker 1>Expect some uh aha moments today.

15
00:00:46.320 --> 00:00:49.240
<v Speaker 2>Absolutely becres he has this great skill, doesn't he, Taking

16
00:00:49.240 --> 00:00:52.799
<v Speaker 2>these really dense technical topics and making them feel well,

17
00:00:52.840 --> 00:00:55.600
<v Speaker 2>almost conversational. This book, it's not just rules. It really

18
00:00:55.640 --> 00:00:58.960
<v Speaker 2>explores how C plus plus talks to memory at its heart.

19
00:00:59.000 --> 00:00:59.880
<v Speaker 2>It's quite empowering.

20
00:01:00.000 --> 00:01:02.719
<v Speaker 1>Actually totally agree. So let's start right at the beginning,

21
00:01:02.920 --> 00:01:06.640
<v Speaker 1>the building blocks. What's the absolute most fundamental thing we

22
00:01:06.680 --> 00:01:09.760
<v Speaker 1>need to get our heads around with C plus plus memory.

23
00:01:10.040 --> 00:01:12.400
<v Speaker 2>Well, what's really interesting is how C plus plus even

24
00:01:12.400 --> 00:01:16.680
<v Speaker 2>defines an object. You know, most people think classes.

25
00:01:16.599 --> 00:01:18.519
<v Speaker 1>Strucks, right, yeah, that's what I think.

26
00:01:18.599 --> 00:01:21.000
<v Speaker 2>But in C plus plus AC it's actually much broader.

27
00:01:21.319 --> 00:01:24.280
<v Speaker 2>Even basic types like an int or a float they're

28
00:01:24.280 --> 00:01:25.439
<v Speaker 2>considered objects too.

29
00:01:25.719 --> 00:01:29.000
<v Speaker 1>Wait, really an int is an object? How does that work?

30
00:01:29.159 --> 00:01:32.439
<v Speaker 2>It's about having certain properties to be an object. In

31
00:01:32.560 --> 00:01:36.200
<v Speaker 2>C plus plus FAG, something needs an address in memory,

32
00:01:36.599 --> 00:01:40.000
<v Speaker 2>it needs to take up some space non zero storage

33
00:01:40.359 --> 00:01:42.719
<v Speaker 2>that has a lifetime, maybe a name, definitely a type,

34
00:01:42.959 --> 00:01:45.599
<v Speaker 2>and a storage duration. In fact, you can actually write

35
00:01:45.760 --> 00:01:49.719
<v Speaker 2>static assert sdd dot ice object vint and well it'll

36
00:01:49.719 --> 00:01:50.840
<v Speaker 2>compile just fine. Huh.

37
00:01:51.000 --> 00:01:53.560
<v Speaker 1>Okay, so even a simple nt has all these characteristics.

38
00:01:53.560 --> 00:01:54.359
<v Speaker 1>That's foundational.

39
00:01:54.480 --> 00:01:56.439
<v Speaker 2>It really is understanding that helps make sense of a

40
00:01:56.480 --> 00:01:57.200
<v Speaker 2>lot of other things.

41
00:01:57.359 --> 00:01:59.680
<v Speaker 1>Okay, so everything has its place, it's time and memory.

42
00:02:00.000 --> 00:02:04.200
<v Speaker 1>Speaking of places, pointers they're everywhere in C plus plus

43
00:02:04.239 --> 00:02:07.959
<v Speaker 1>C right, even if the book mentions kind of usingly

44
00:02:08.120 --> 00:02:11.439
<v Speaker 1>that a strict definition is surprisingly hard to come by

45
00:02:11.919 --> 00:02:12.599
<v Speaker 1>in the standard.

46
00:02:12.680 --> 00:02:17.759
<v Speaker 2>Exactly. Pointers are incredibly powerful tools, but that power comes

47
00:02:17.800 --> 00:02:20.560
<v Speaker 2>with risk. Put it arithmetic for example, you can write

48
00:02:21.039 --> 00:02:24.319
<v Speaker 2>it q qles P plus three, no problem. But if

49
00:02:24.360 --> 00:02:27.840
<v Speaker 2>P isn't pointing where you think it is, trouble, big trouble.

50
00:02:28.439 --> 00:02:30.919
<v Speaker 2>Q could point to some arbitrary location. You could cause

51
00:02:30.960 --> 00:02:35.159
<v Speaker 2>serious damage. And the responsibility for managing that power it

52
00:02:35.199 --> 00:02:38.479
<v Speaker 2>always falls back on the C plus plus programmer. Always right.

53
00:02:38.680 --> 00:02:41.840
<v Speaker 1>With great power, it comes great responsibility. As they say, okay,

54
00:02:42.000 --> 00:02:44.639
<v Speaker 1>what about void? I see that sometimes in like lower

55
00:02:44.680 --> 00:02:45.479
<v Speaker 1>level code.

56
00:02:45.240 --> 00:02:48.360
<v Speaker 2>Ah void. Think of it as just a raw memory address.

57
00:02:48.599 --> 00:02:50.759
<v Speaker 2>It tells you where something is, but not what it is.

58
00:02:51.280 --> 00:02:54.240
<v Speaker 2>It has no type information attached. Any other pointer type

59
00:02:54.240 --> 00:02:56.599
<v Speaker 2>can convert to void, but going back the other way

60
00:02:56.879 --> 00:02:59.919
<v Speaker 2>usually needs an explicit cast, basically saying here's a location,

61
00:03:00.319 --> 00:03:02.159
<v Speaker 2>but I make no promises about what's there.

62
00:03:02.520 --> 00:03:05.599
<v Speaker 1>Got it. So beyond just what an object is, let's

63
00:03:05.599 --> 00:03:11.000
<v Speaker 1>talk about its life in memory, lifetime, size, alignment padding.

64
00:03:11.560 --> 00:03:14.159
<v Speaker 1>How does C plus plus manage lifetime?

65
00:03:14.680 --> 00:03:17.919
<v Speaker 2>Lifetime is absolutely key. Automatic objects, the ones usually on

66
00:03:17.960 --> 00:03:21.919
<v Speaker 2>the stack, have very predictable, deterministic lifetimes. They get created,

67
00:03:21.960 --> 00:03:24.719
<v Speaker 2>they go out of scope, Boom, they're destructed. Static objects

68
00:03:24.759 --> 00:03:28.199
<v Speaker 2>live for pretty much the whole program run. But dynamically

69
00:03:28.240 --> 00:03:31.719
<v Speaker 2>allocated objects, the ones you create with new. Their lifetime

70
00:03:31.840 --> 00:03:34.960
<v Speaker 2>is well when your program says so, and that's where

71
00:03:35.000 --> 00:03:35.759
<v Speaker 2>things can go wrong.

72
00:03:35.879 --> 00:03:38.680
<v Speaker 1>Ah okay, So if it's when your program says so,

73
00:03:38.680 --> 00:03:41.439
<v Speaker 1>I'm guessing the classic mistake is forgetting to say it's

74
00:03:41.479 --> 00:03:42.840
<v Speaker 1>over right, forgetting to delete.

75
00:03:42.840 --> 00:03:45.240
<v Speaker 2>That's the number one pitfall. Absolutely, you call new, you

76
00:03:45.240 --> 00:03:48.199
<v Speaker 2>get your object, it's constructor runs. But if you forget delete,

77
00:03:48.240 --> 00:03:51.560
<v Speaker 2>the destructor never runs, and the memory it just leaks away.

78
00:03:51.599 --> 00:03:53.639
<v Speaker 2>Resource leak, memory leak, bad news.

79
00:03:53.800 --> 00:03:56.800
<v Speaker 1>Right. Okay, Now this next bit sounds really interesting. Size

80
00:03:56.840 --> 00:04:00.360
<v Speaker 1>alignment and padding using size isn't always straightforward.

81
00:03:59.840 --> 00:04:01.840
<v Speaker 2>No, so it often surprises people. This is a great

82
00:04:01.840 --> 00:04:05.159
<v Speaker 2>aha moment. Let's take a simple class class X charcis

83
00:04:05.240 --> 00:04:06.439
<v Speaker 2>short S and tn.

84
00:04:06.759 --> 00:04:11.280
<v Speaker 1>Okay, a char a short an int, So like one

85
00:04:11.280 --> 00:04:13.560
<v Speaker 1>by plus two bytes plus four bytes seven bytes.

86
00:04:13.599 --> 00:04:15.960
<v Speaker 2>That's what you'd intuitively think, right, But if you actually

87
00:04:16.040 --> 00:04:19.000
<v Speaker 2>check size of x, you'll probably find it's eight bytes eight.

88
00:04:19.240 --> 00:04:20.720
<v Speaker 1>Where does the extra byte come from?

89
00:04:20.920 --> 00:04:24.720
<v Speaker 2>Comes from? Padding bytes? Compilers often add these invisible bytes

90
00:04:24.720 --> 00:04:27.639
<v Speaker 2>between members, So between C and S there might be

91
00:04:27.680 --> 00:04:31.360
<v Speaker 2>a padding byte between S and N maybe two padding bytes.

92
00:04:31.680 --> 00:04:37.240
<v Speaker 2>Why performance exactly? Performance CPU's access data much more efficiently

93
00:04:37.319 --> 00:04:39.920
<v Speaker 2>when it's aligned on certain boundaries, like a four byte

94
00:04:39.920 --> 00:04:42.680
<v Speaker 2>boundary for an ind So the compiler adds padding to

95
00:04:42.800 --> 00:04:45.000
<v Speaker 2>ensure each member starts at an optimal address.

96
00:04:45.160 --> 00:04:48.199
<v Speaker 1>Okay, that makes sense, But the book also mentions trailing

97
00:04:48.240 --> 00:04:50.279
<v Speaker 1>padding bytes patting at the end of the object.

98
00:04:50.279 --> 00:04:53.879
<v Speaker 2>Why ah, that's specifically for arrays of these objects. Remember,

99
00:04:53.959 --> 00:04:56.079
<v Speaker 2>array elements have to sit right next to each other

100
00:04:56.120 --> 00:04:59.800
<v Speaker 2>in memory contiguously. That trailing padding ensures that if array

101
00:04:59.839 --> 00:05:02.560
<v Speaker 2>is properly aligned, then R one will also start on

102
00:05:02.600 --> 00:05:05.759
<v Speaker 2>the correct alignment boundary, and so on. It's subtle, but

103
00:05:05.839 --> 00:05:07.920
<v Speaker 2>crucial for array correctness and performance.

104
00:05:08.360 --> 00:05:10.959
<v Speaker 1>Wow. Okay, so memory isn't quite as packed as you

105
00:05:11.040 --> 00:05:13.920
<v Speaker 1>might think. This has got to affect how C plus

106
00:05:13.959 --> 00:05:15.920
<v Speaker 1>plus copies and moves objects around.

107
00:05:15.959 --> 00:05:20.519
<v Speaker 2>Surely, Oh, absolutely, Copy and move operations constructors assignment operators

108
00:05:20.839 --> 00:05:23.600
<v Speaker 2>are deeply affected by this, especially when you write your

109
00:05:23.639 --> 00:05:27.279
<v Speaker 2>own classes. For a really simple struct maybe like point

110
00:05:27.279 --> 00:05:31.079
<v Speaker 2>two d just two floats, the compiler generated copy and

111
00:05:31.120 --> 00:05:33.800
<v Speaker 2>move are usually fine, But if you have a class

112
00:05:33.839 --> 00:05:36.800
<v Speaker 2>like say naive string, that manages its own raw memory

113
00:05:36.879 --> 00:05:39.759
<v Speaker 2>like a character array, then the defaults are not. Okay,

114
00:05:39.959 --> 00:05:42.720
<v Speaker 2>you absolutely need to write your own destructor copy constructor

115
00:05:42.759 --> 00:05:45.480
<v Speaker 2>copy assignment. That's the classic rule of three and.

116
00:05:45.439 --> 00:05:47.800
<v Speaker 1>The rule of five. If you add move operations, which

117
00:05:47.800 --> 00:05:50.360
<v Speaker 1>is pretty standard now in modern C plus.

118
00:05:50.120 --> 00:05:54.040
<v Speaker 2>Plus, precisely you need custom logic to handle copying or

119
00:05:54.079 --> 00:05:56.279
<v Speaker 2>moving that raw memory correctly, Otherwise you end up with

120
00:05:56.560 --> 00:05:58.839
<v Speaker 2>shallow copies, double deletes all sorts of problems.

121
00:05:58.879 --> 00:06:01.839
<v Speaker 1>And the book mentions s TD dot exchange as a

122
00:06:01.879 --> 00:06:03.519
<v Speaker 1>helper for writing move constructors.

123
00:06:03.839 --> 00:06:06.959
<v Speaker 2>Yeah, std dot exchange is neat little utility. It basically

124
00:06:07.040 --> 00:06:09.639
<v Speaker 2>lets you swap the resource pointer in your move constructor.

125
00:06:09.680 --> 00:06:12.839
<v Speaker 2>More cleanly, it assigns a new value like null ptr

126
00:06:13.040 --> 00:06:15.439
<v Speaker 2>to the source objects pointer and gives you back the

127
00:06:15.480 --> 00:06:18.680
<v Speaker 2>old value the resource pointer, all in one go. Makes

128
00:06:18.720 --> 00:06:21.000
<v Speaker 2>the code a bit tidier less error prone.

129
00:06:21.079 --> 00:06:24.279
<v Speaker 1>Okay, and while we're on data layout, what about basic

130
00:06:24.680 --> 00:06:27.639
<v Speaker 1>built in arrays raw rays? They're just sequences in memory, right,

131
00:06:27.800 --> 00:06:30.120
<v Speaker 1>and pointer arithmetic is key there too, Exactly.

132
00:06:30.240 --> 00:06:34.639
<v Speaker 2>Raw rays are contiguous sequences, and accessing RA is basically

133
00:06:34.639 --> 00:06:37.839
<v Speaker 2>the same as writing R plus I. The crucial thing

134
00:06:37.959 --> 00:06:40.439
<v Speaker 2>is that the plus i isn't just adding ie bytes.

135
00:06:40.480 --> 00:06:43.319
<v Speaker 2>It's typed. It means add i times the size of

136
00:06:43.360 --> 00:06:45.800
<v Speaker 2>one element. So if it's an array events, it adds

137
00:06:45.839 --> 00:06:48.319
<v Speaker 2>i size of bytes. That's how it steps correctly from

138
00:06:48.319 --> 00:06:49.279
<v Speaker 2>one element to the next.

139
00:06:49.360 --> 00:06:51.040
<v Speaker 1>Right, it knows the size of the things in the

140
00:06:51.120 --> 00:06:52.000
<v Speaker 1>array precisely.

141
00:06:52.079 --> 00:06:54.079
<v Speaker 2>Oh. In an interesting side note, new and darro is

142
00:06:54.120 --> 00:06:57.720
<v Speaker 2>actually allowed, you can dynamically allocate a zero sized array.

143
00:06:58.160 --> 00:07:01.439
<v Speaker 2>Doesn't sound useful, but it can simple some generic code sometimes.

144
00:07:01.680 --> 00:07:04.920
<v Speaker 1>Okay, we've got the building blocks. Now let's walk down

145
00:07:04.959 --> 00:07:08.040
<v Speaker 1>what the book calls the perilous path things to be

146
00:07:08.160 --> 00:07:10.959
<v Speaker 1>really careful with. It talks about different kinds of evil

147
00:07:11.000 --> 00:07:16.360
<v Speaker 1>in C plus plus. What let's start with. IFNDR sounds ominous.

148
00:07:16.000 --> 00:07:21.199
<v Speaker 2>IFNDR ill formed no diagnostic required, it's insidious. It means

149
00:07:21.199 --> 00:07:23.839
<v Speaker 2>your code is technically broken according to the C plus

150
00:07:23.839 --> 00:07:26.839
<v Speaker 2>plus standard, but the compiler isn't actually required to warn

151
00:07:26.879 --> 00:07:27.360
<v Speaker 2>you about it.

152
00:07:27.800 --> 00:07:30.639
<v Speaker 1>So it might compile fine, but just not work, or

153
00:07:30.720 --> 00:07:32.560
<v Speaker 1>work sometimes.

154
00:07:31.959 --> 00:07:36.040
<v Speaker 2>Exactly silent failures really hard to debug. The compiler might

155
00:07:36.120 --> 00:07:38.439
<v Speaker 2>not have enough info compile time, or the standard just

156
00:07:38.480 --> 00:07:42.399
<v Speaker 2>gives it a pass on issuing a diagnostic. Very dangerous, okay.

157
00:07:42.439 --> 00:07:45.480
<v Speaker 1>And then the big one, undefined behavior UB everyone talks

158
00:07:45.480 --> 00:07:46.480
<v Speaker 1>about ub ah.

159
00:07:46.600 --> 00:07:50.399
<v Speaker 2>Yes, UB undefined behavior means the C plus plus standard

160
00:07:50.399 --> 00:07:53.680
<v Speaker 2>places absolutely no requirement on what happens if your code

161
00:07:53.720 --> 00:07:56.319
<v Speaker 2>triggers it. Anything could happen. It might crash, it might

162
00:07:56.360 --> 00:07:59.319
<v Speaker 2>give the wrong answer, it might format your hard drive. Okay,

163
00:07:59.360 --> 00:08:02.000
<v Speaker 2>probably not that last one, but technically the standard doesn't

164
00:08:02.000 --> 00:08:02.399
<v Speaker 2>forbid it.

165
00:08:02.480 --> 00:08:05.439
<v Speaker 1>So like dear referencing a null pointer or using an

166
00:08:05.519 --> 00:08:07.480
<v Speaker 1>uninitialized variable, those are.

167
00:08:07.399 --> 00:08:12.360
<v Speaker 2>The classic examples. Yes, dear referencing null, using uninitialized memory,

168
00:08:12.560 --> 00:08:16.639
<v Speaker 2>accessing a raise out of bounds, signed integer overflow. The

169
00:08:16.680 --> 00:08:19.000
<v Speaker 2>list goes on. If you hit UB, you are in

170
00:08:19.120 --> 00:08:21.759
<v Speaker 2>serious trouble. As the book puts it, all bets are off.

171
00:08:21.800 --> 00:08:24.040
<v Speaker 1>And the scary part is how compilers treat it. Right,

172
00:08:24.079 --> 00:08:25.199
<v Speaker 1>they optimize around it.

173
00:08:25.279 --> 00:08:28.079
<v Speaker 2>That's the really unsettling part. Compilers assume that correct C

174
00:08:28.160 --> 00:08:31.360
<v Speaker 2>plus plus code never contains UB, so if they see

175
00:08:31.360 --> 00:08:34.200
<v Speaker 2>potentially UB, they might just optimize assuming it can't happen.

176
00:08:34.360 --> 00:08:37.320
<v Speaker 2>They might remove null checks because hey, you wouldn't dare

177
00:08:37.320 --> 00:08:41.279
<v Speaker 2>reference null later, right. They might reorder operations or eliminate

178
00:08:41.440 --> 00:08:44.799
<v Speaker 2>entire loops based on assumptions flowing from potentially UB. It

179
00:08:44.879 --> 00:08:46.759
<v Speaker 2>leads to sometimes surprising effects.

180
00:08:46.879 --> 00:08:49.639
<v Speaker 1>To put it mildly, wow, So UB isn't just It

181
00:08:49.720 --> 00:08:52.559
<v Speaker 1>might crash. It's the compiler might silently break my code

182
00:08:52.600 --> 00:08:53.440
<v Speaker 1>in weird ways.

183
00:08:53.320 --> 00:08:56.120
<v Speaker 2>Precisely treat you b as a critical bug. Always don't

184
00:08:56.120 --> 00:08:58.720
<v Speaker 2>rely on it working on your specific compiler version.

185
00:08:58.799 --> 00:09:02.759
<v Speaker 1>Okay, IFNDR, what about the one definition rule the ODR.

186
00:09:03.080 --> 00:09:05.200
<v Speaker 1>That sounds important, but maybe less evil.

187
00:09:05.440 --> 00:09:10.080
<v Speaker 2>The ODR is crucial, but yeah, maybe less overtly evil,

188
00:09:10.519 --> 00:09:15.159
<v Speaker 2>more subtly problematic if violated. It basically says that anything

189
00:09:15.200 --> 00:09:18.480
<v Speaker 2>in your program, a function, a class, a template, a

190
00:09:18.559 --> 00:09:23.039
<v Speaker 2>global variable must have exactly one definition in the entire program.

191
00:09:23.080 --> 00:09:26.200
<v Speaker 1>And if you have two definitions, say in different.

192
00:09:25.919 --> 00:09:29.600
<v Speaker 2>Files, then you're often in IFNDR territory again, or the

193
00:09:29.679 --> 00:09:32.960
<v Speaker 2>linker might just silently pick one definition and ignore the other.

194
00:09:33.639 --> 00:09:36.840
<v Speaker 2>This can lead to absolute chaos, especially with templates or

195
00:09:36.879 --> 00:09:40.799
<v Speaker 2>inline functions defined slightly differently in headers included in different places,

196
00:09:41.000 --> 00:09:42.720
<v Speaker 2>very hard to track down bugs.

197
00:09:42.519 --> 00:09:45.360
<v Speaker 1>And implementation defined behavior. How does that fit in?

198
00:09:45.519 --> 00:09:50.000
<v Speaker 2>That's different? Implementation defined means the standard allows different behaviors,

199
00:09:50.159 --> 00:09:53.120
<v Speaker 2>but requires the compiler vendor to document exactly what their

200
00:09:53.159 --> 00:09:54.200
<v Speaker 2>implementation does.

201
00:09:54.320 --> 00:09:57.440
<v Speaker 1>So it's predictable on one compiler, but maybe not portable.

202
00:09:57.159 --> 00:09:59.600
<v Speaker 2>Exactly, Like the size of NT or the result of

203
00:09:59.679 --> 00:10:02.879
<v Speaker 2>right fifting a negative number. It's not inherently evil, just

204
00:10:02.919 --> 00:10:04.480
<v Speaker 2>something to be aware of if you need your code

205
00:10:04.480 --> 00:10:05.679
<v Speaker 2>to work identically everywhere.

206
00:10:05.799 --> 00:10:10.320
<v Speaker 1>Okay, moving on to specific dangerous things point or maneuvers

207
00:10:10.960 --> 00:10:14.879
<v Speaker 1>beyond just adding random numbers. The book mentions type punning.

208
00:10:15.279 --> 00:10:19.480
<v Speaker 2>What's that type punning? It's essentially trying to subvert the

209
00:10:19.519 --> 00:10:22.279
<v Speaker 2>type system. As the book says, you have memory representing

210
00:10:22.320 --> 00:10:24.960
<v Speaker 2>one type, say a float, and you try to access

211
00:10:24.960 --> 00:10:27.519
<v Speaker 2>it as if it were a different type, say an int,

212
00:10:27.799 --> 00:10:29.720
<v Speaker 2>to reinterpret its raw bits.

213
00:10:30.120 --> 00:10:31.919
<v Speaker 1>How do people usually try to do this?

214
00:10:32.159 --> 00:10:35.600
<v Speaker 2>A common but usually illegal way in C plus plus

215
00:10:35.679 --> 00:10:38.559
<v Speaker 2>plus S is using a union. You have a union

216
00:10:38.600 --> 00:10:41.720
<v Speaker 2>with maybe float F and an N you're at to UDAF.

217
00:10:41.759 --> 00:10:43.399
<v Speaker 2>Then you immediately try to read you in.

218
00:10:43.279 --> 00:10:45.559
<v Speaker 1>And that's ub even though they share the same memory.

219
00:10:45.679 --> 00:10:48.600
<v Speaker 2>Generally, yes, that's UBN C plus plus here. Reading from

220
00:10:48.639 --> 00:10:50.840
<v Speaker 2>a union member that wasn't the last one written to

221
00:10:50.960 --> 00:10:53.480
<v Speaker 2>is undefined unless you're dealing with what's called the common

222
00:10:53.559 --> 00:10:54.440
<v Speaker 2>initial sequence.

223
00:10:54.519 --> 00:10:55.559
<v Speaker 1>Common initial sequence.

224
00:10:55.639 --> 00:10:58.200
<v Speaker 2>Yeah, it's a narrow exception. If two structs or classes

225
00:10:58.200 --> 00:10:59.919
<v Speaker 2>in the union start with the same sequence of men

226
00:11:00.279 --> 00:11:02.279
<v Speaker 2>types like both start with an innt followed by a char.

227
00:11:02.519 --> 00:11:04.320
<v Speaker 2>You can write to one and then read that common

228
00:11:04.320 --> 00:11:07.679
<v Speaker 2>sequence part through the other, but it's complex. Relying on

229
00:11:07.799 --> 00:11:09.879
<v Speaker 2>union penning is usually asking for trouble.

230
00:11:10.120 --> 00:11:13.759
<v Speaker 1>What about using std dot memppi. I've seen that used

231
00:11:13.799 --> 00:11:15.360
<v Speaker 1>for reinterpreting.

232
00:11:14.639 --> 00:11:18.279
<v Speaker 2>Bits, right, mechpapie is often suggested. It can be used,

233
00:11:18.360 --> 00:11:20.360
<v Speaker 2>but you have to be careful. If you mempi the

234
00:11:20.360 --> 00:11:22.759
<v Speaker 2>bytes of a float into some raw memory buffer and

235
00:11:22.759 --> 00:11:24.919
<v Speaker 2>then immediately try to read that buffer using an INT,

236
00:11:25.159 --> 00:11:28.639
<v Speaker 2>that's still you b why because mechpi doesn't magically start

237
00:11:28.679 --> 00:11:30.639
<v Speaker 2>the lifetime of nnt object there. It might start the

238
00:11:30.720 --> 00:11:33.080
<v Speaker 2>lifetime of a float if the conditions are right, but

239
00:11:33.200 --> 00:11:36.399
<v Speaker 2>accessing it via ND violates type rules.

240
00:11:36.519 --> 00:11:38.759
<v Speaker 1>So how do you use megpi correctly for this?

241
00:11:39.200 --> 00:11:41.399
<v Speaker 2>The safe way is to magpie the bytes into an

242
00:11:41.399 --> 00:11:44.080
<v Speaker 2>already existing object of the target type, so you'd have

243
00:11:44.120 --> 00:11:46.799
<v Speaker 2>an invariable already and you may compile the float's bites

244
00:11:46.840 --> 00:11:49.600
<v Speaker 2>into the memory location of that INT. That's generally okay.

245
00:11:49.799 --> 00:11:52.799
<v Speaker 1>This is getting really low level. Are there specific types

246
00:11:52.840 --> 00:11:54.600
<v Speaker 1>for messing with addresses as numbers?

247
00:11:54.679 --> 00:11:58.039
<v Speaker 2>Yes, C plus plus provides std dot ntptr t and

248
00:11:58.240 --> 00:12:02.000
<v Speaker 2>ftd dot untptr. These are integer types guaranteed to be

249
00:12:02.080 --> 00:12:04.279
<v Speaker 2>large enough to hold a pointer value. You can cast

250
00:12:04.320 --> 00:12:06.840
<v Speaker 2>a pointer to one of these, do some integer math, maybe,

251
00:12:06.840 --> 00:12:09.279
<v Speaker 2>and then cast it back, But the book warns they

252
00:12:09.320 --> 00:12:13.679
<v Speaker 2>are inherently fragile and non portable. Things like bait order

253
00:12:13.919 --> 00:12:17.759
<v Speaker 2>indianness can bite you. If you're storing addresses as integers,

254
00:12:18.159 --> 00:12:20.759
<v Speaker 2>you're really operating outside the safety of the type system.

255
00:12:21.519 --> 00:12:24.960
<v Speaker 2>Use with extreme caution, usually only needed for very specific,

256
00:12:25.039 --> 00:12:26.080
<v Speaker 2>low level tasks.

257
00:12:26.600 --> 00:12:30.200
<v Speaker 1>Right, Okay, we've seen the dangers. Let's talk about tools

258
00:12:30.200 --> 00:12:33.120
<v Speaker 1>for control. Casting spells as the book calls them. How

259
00:12:33.120 --> 00:12:36.799
<v Speaker 1>should we think about casts? Are we lying to the compiler?

260
00:12:37.159 --> 00:12:39.679
<v Speaker 2>That's a common misconception. Yeah, It's better to think of

261
00:12:39.759 --> 00:12:43.759
<v Speaker 2>casts not as lying, but as adjusting the compiler's view.

262
00:12:44.080 --> 00:12:46.639
<v Speaker 2>You're explicitly telling the compiler. I know you see this

263
00:12:46.720 --> 00:12:49.200
<v Speaker 2>expression as type A, but I want you to treat

264
00:12:49.200 --> 00:12:52.240
<v Speaker 2>it as type B for this specific operation. You're signaling

265
00:12:52.279 --> 00:12:52.759
<v Speaker 2>your intent.

266
00:12:52.919 --> 00:12:55.279
<v Speaker 1>Okay, makes sense. Let's go through the C plus plus

267
00:12:55.320 --> 00:12:58.639
<v Speaker 1>name casts. First. Stato cast. The book calls it our

268
00:12:58.679 --> 00:12:59.600
<v Speaker 1>best friend.

269
00:12:59.519 --> 00:13:02.200
<v Speaker 2>And for good reason. Static cast is for well defined

270
00:13:02.320 --> 00:13:05.519
<v Speaker 2>or reasonably safe conversions things the compiler could often do

271
00:13:05.559 --> 00:13:09.679
<v Speaker 2>implicitly or standard conversions between related types like in to

272
00:13:09.799 --> 00:13:13.320
<v Speaker 2>float or importantly, casting a pointer from a drive class

273
00:13:13.320 --> 00:13:15.480
<v Speaker 2>to its public base class drive it base.

274
00:13:15.759 --> 00:13:18.919
<v Speaker 1>Does stato caast ever change the actual memory address?

275
00:13:19.120 --> 00:13:21.480
<v Speaker 2>Yes, it can. That's a key point. Ye. If use

276
00:13:21.519 --> 00:13:23.679
<v Speaker 2>static cast to drive to a base in a multiple

277
00:13:23.720 --> 00:13:28.039
<v Speaker 2>inheritance scenario, the address might actually change. The base subobject

278
00:13:28.080 --> 00:13:30.000
<v Speaker 2>might not be located at the very beginning of the

279
00:13:30.080 --> 00:13:34.279
<v Speaker 2>drived object and memory. Stata cast handles that address adjustment correctly.

280
00:13:34.399 --> 00:13:39.039
<v Speaker 1>Okay, next dynamic cast, the book says it's assigned. Something's wrong.

281
00:13:39.519 --> 00:13:40.320
<v Speaker 1>That's pretty strong.

282
00:13:40.440 --> 00:13:43.360
<v Speaker 2>It often is assigned. Yeah. Yeah. Dynamic cast is used

283
00:13:43.360 --> 00:13:47.080
<v Speaker 2>for navigating class hierarchies at runtime, typically casting down from

284
00:13:47.080 --> 00:13:49.679
<v Speaker 2>a bates class point of reference to a drive class

285
00:13:49.679 --> 00:13:53.799
<v Speaker 2>point of reference. It needs run time type information RTTI enabled,

286
00:13:53.879 --> 00:13:57.519
<v Speaker 2>which adds overhead, and if the cast fails because the

287
00:13:57.559 --> 00:14:00.480
<v Speaker 2>object isn't actually of the target drive type, it returns

288
00:14:00.559 --> 00:14:04.159
<v Speaker 2>null ptr for pointers or throws std dot bad cast

289
00:14:04.200 --> 00:14:04.919
<v Speaker 2>for references.

290
00:14:05.080 --> 00:14:06.639
<v Speaker 1>So why is it assigned? Something's wrong?

291
00:14:06.759 --> 00:14:09.720
<v Speaker 2>Because heavy reliance on dynamic cast often suggests your class

292
00:14:09.759 --> 00:14:12.279
<v Speaker 2>design might be flawed. You might be better off using

293
00:14:12.360 --> 00:14:16.200
<v Speaker 2>virtual functions polymorphism to achieve the desired behavior without needing

294
00:14:16.200 --> 00:14:18.840
<v Speaker 2>to know the exact drived type at runtime. The book

295
00:14:18.879 --> 00:14:21.759
<v Speaker 2>suggests its use implies a potentially perfectible interface.

296
00:14:22.120 --> 00:14:25.840
<v Speaker 1>Got it, then const cast playing tricks with safety.

297
00:14:26.559 --> 00:14:29.960
<v Speaker 2>Yeah. Constcast is the only cast that can remove cost

298
00:14:30.360 --> 00:14:34.200
<v Speaker 2>or volatile. Its main legitimate use is often dealing with

299
00:14:34.279 --> 00:14:39.000
<v Speaker 2>older APIs or libraries that weren't const correct. You might

300
00:14:39.039 --> 00:14:40.799
<v Speaker 2>have a const object but need to pass it to

301
00:14:40.840 --> 00:14:44.360
<v Speaker 2>a function that inexplicably takes a non constant pointer, even

302
00:14:44.399 --> 00:14:46.679
<v Speaker 2>though you know the function won't actually modify it. But

303
00:14:46.759 --> 00:14:50.679
<v Speaker 2>it's dangerous, extremely dangerous if misused. If the object you're

304
00:14:50.679 --> 00:14:54.000
<v Speaker 2>casting away consts from was originally declared as cost, then

305
00:14:54.120 --> 00:14:56.759
<v Speaker 2>actually trying to modify it through the constcast at pointer

306
00:14:57.000 --> 00:15:01.159
<v Speaker 2>is undefined behavior. You're breaking a fundamental primt use it very,

307
00:15:01.360 --> 00:15:02.080
<v Speaker 2>very sparingly.

308
00:15:02.320 --> 00:15:06.279
<v Speaker 1>And finally, the most feared one reinterpret cast. Believe me

309
00:15:06.320 --> 00:15:07.360
<v Speaker 1>a compiler.

310
00:15:07.000 --> 00:15:09.720
<v Speaker 2>That sums it up perfectly. Reinterpret cast does a raw

311
00:15:09.879 --> 00:15:13.440
<v Speaker 2>bit level reinterpretation between pointer types or pointers and integers.

312
00:15:13.720 --> 00:15:16.320
<v Speaker 2>It tails a compiler forget types. Just treat these bits

313
00:15:16.360 --> 00:15:19.240
<v Speaker 2>as if they were this other pointer type m key danger.

314
00:15:19.639 --> 00:15:23.080
<v Speaker 2>It does no address adjustments. Remember how static cast might

315
00:15:23.080 --> 00:15:26.799
<v Speaker 2>adjust the address for base classes. Reinterpret cast doesn't, so.

316
00:15:26.759 --> 00:15:28.799
<v Speaker 1>If you reinterpret cast a derived to a base in

317
00:15:28.919 --> 00:15:30.799
<v Speaker 1>multiple inheritance, you'll.

318
00:15:30.600 --> 00:15:33.120
<v Speaker 2>Likely get the wrong addressed for the base subobject, and

319
00:15:33.200 --> 00:15:36.799
<v Speaker 2>trying to use that pointer leads straight to undefined behavior.

320
00:15:37.279 --> 00:15:41.440
<v Speaker 2>It's incredibly powerful for low level bit manipulation, but incredibly

321
00:15:41.559 --> 00:15:45.799
<v Speaker 2>easy to misuse with disastrous consequences. Only use it if

322
00:15:45.799 --> 00:15:48.120
<v Speaker 2>you absolutely know what you're doing at the bit level.

323
00:15:48.159 --> 00:15:49.679
<v Speaker 1>Any other is worth mentioning quickly.

324
00:15:49.840 --> 00:15:53.399
<v Speaker 2>The book also notes STD dot bitcast from C plus

325
00:15:53.399 --> 00:15:55.559
<v Speaker 2>plus twenty, which is a safer way to do the

326
00:15:55.600 --> 00:15:59.759
<v Speaker 2>type punting reinterpretation for object types copying bits safely, and

327
00:16:00.120 --> 00:16:03.399
<v Speaker 2>STD do duration cast for working with chronotime durations, which

328
00:16:03.440 --> 00:16:04.720
<v Speaker 2>is very useful and safe.

329
00:16:04.879 --> 00:16:08.440
<v Speaker 1>Okay, And the last cast, the reviled one, the C

330
00:16:08.639 --> 00:16:11.799
<v Speaker 1>style cast TX a prick. Why is it so bad

331
00:16:11.840 --> 00:16:12.759
<v Speaker 1>in C plus.

332
00:16:12.480 --> 00:16:15.440
<v Speaker 2>Plus h several reasons. One, it's hard to search foreign

333
00:16:15.519 --> 00:16:18.759
<v Speaker 2>coode compared to the name C plus plus casts, two,

334
00:16:18.919 --> 00:16:22.320
<v Speaker 2>and most importantly, it lacks intent. A C style cast

335
00:16:22.360 --> 00:16:25.440
<v Speaker 2>tries to be smart and will attempt to sequence of conversions,

336
00:16:25.559 --> 00:16:28.559
<v Speaker 2>often ending up doing a stata cast, a costcast, or

337
00:16:28.600 --> 00:16:31.080
<v Speaker 2>even a reinterpret cast, depending on the types involved.

338
00:16:31.120 --> 00:16:33.200
<v Speaker 1>So am I do a dangerous reinterpret cast when you

339
00:16:33.240 --> 00:16:34.679
<v Speaker 1>didn't explicitly mean.

340
00:16:34.519 --> 00:16:38.600
<v Speaker 2>It to exactly? It hides the potential danger. Using the

341
00:16:38.639 --> 00:16:41.879
<v Speaker 2>specific C plus plus casts forced you to think about

342
00:16:41.879 --> 00:16:44.240
<v Speaker 2>what kind of conversion you actually need and makes your

343
00:16:44.279 --> 00:16:47.639
<v Speaker 2>intent clear to other readers and your future self. Avoid

344
00:16:47.759 --> 00:16:49.440
<v Speaker 2>C style casts in modern C.

345
00:16:49.480 --> 00:16:52.799
<v Speaker 1>Plus plus right clear intent is key. Okay, we've seen

346
00:16:52.799 --> 00:16:56.360
<v Speaker 1>the tools the dangers. Now let's talk about taking responsibility

347
00:16:56.440 --> 00:17:00.759
<v Speaker 1>C plus plus style RAII and smart pointers. The book

348
00:17:00.879 --> 00:17:03.360
<v Speaker 1>really emphasizes the power of destructors here.

349
00:17:03.759 --> 00:17:06.839
<v Speaker 2>Destructors are arguably the killer feature of C plus plus

350
00:17:06.839 --> 00:17:11.960
<v Speaker 2>for resource management. They enable RAII. Resource acquisition is initialization,

351
00:17:12.440 --> 00:17:14.440
<v Speaker 2>though as the book chekily points out, the name is

352
00:17:14.440 --> 00:17:16.839
<v Speaker 2>a bit misleading. It's really more to do with destructors

353
00:17:16.839 --> 00:17:20.599
<v Speaker 2>than constructors. How So, the core idea is you acquire

354
00:17:20.640 --> 00:17:23.279
<v Speaker 2>a resource like memory, a file handle, a lock in

355
00:17:23.319 --> 00:17:27.279
<v Speaker 2>an object's constructor. That object now owns the resource. When

356
00:17:27.319 --> 00:17:29.519
<v Speaker 2>the object's light time ends, usually when it goes out

357
00:17:29.559 --> 00:17:32.599
<v Speaker 2>of scope, its destructor is automatically called, and the destructor's

358
00:17:32.680 --> 00:17:34.119
<v Speaker 2>job is to release the resource.

359
00:17:34.400 --> 00:17:37.240
<v Speaker 1>Ah. So acquisition is tied to object initialization, but the

360
00:17:37.279 --> 00:17:38.559
<v Speaker 1>magic happens at destruction.

361
00:17:39.000 --> 00:17:44.240
<v Speaker 2>Precisely. This automatic cleanup is why some C plus plus luminaries,

362
00:17:44.319 --> 00:17:47.240
<v Speaker 2>as the book calls them, say, the most beautiful instruction

363
00:17:47.359 --> 00:17:50.359
<v Speaker 2>in C plus plus is the closing brace that marks

364
00:17:50.359 --> 00:17:52.559
<v Speaker 2>the end of a scope triggering destructors.

365
00:17:52.640 --> 00:17:56.000
<v Speaker 1>Because it guarantees cleanup even if errors happen exactly.

366
00:17:56.880 --> 00:18:01.359
<v Speaker 2>RAII makes code naturally exception safe. If an exception is thrown,

367
00:18:01.400 --> 00:18:04.519
<v Speaker 2>the stack on wines, scopes are exited, and the destructors

368
00:18:04.559 --> 00:18:07.960
<v Speaker 2>of local objects are still called, ensuring resources aren't leaked.

369
00:18:08.240 --> 00:18:10.160
<v Speaker 2>It's incredibly powerful and robust.

370
00:18:10.279 --> 00:18:13.599
<v Speaker 1>This leads perfectly into smart pointers, doesn't it. Raw pointers,

371
00:18:13.640 --> 00:18:17.000
<v Speaker 1>as we said, don't convey ownership clearly who's responsible for

372
00:18:17.039 --> 00:18:17.640
<v Speaker 1>the delete.

373
00:18:17.839 --> 00:18:21.039
<v Speaker 2>That's the fundamental problem raw pointers pose for resource management.

374
00:18:21.480 --> 00:18:24.720
<v Speaker 2>Smart pointers solve this by encoding ownership semantics directly into

375
00:18:24.799 --> 00:18:27.319
<v Speaker 2>the type system. They wrap a raw pointer and manage

376
00:18:27.359 --> 00:18:29.279
<v Speaker 2>its lifetime according to specific rules.

377
00:18:29.400 --> 00:18:32.599
<v Speaker 1>Let's start with std dot unique ftr std.

378
00:18:32.319 --> 00:18:37.400
<v Speaker 2>Dot UNIQFTR represents exclusive ownership. It's the single clear last

379
00:18:37.519 --> 00:18:41.079
<v Speaker 2>user of a resource. Only one unique FTR can own

380
00:18:41.079 --> 00:18:44.319
<v Speaker 2>a particular object at any time. It's very lightweight, typically

381
00:18:44.359 --> 00:18:48.480
<v Speaker 2>the same size as a raw pointer. Zero runtime overhead Crucially,

382
00:18:48.519 --> 00:18:51.799
<v Speaker 2>it's noncopyable. You can move ownership from one uniquapture to another,

383
00:18:51.839 --> 00:18:54.599
<v Speaker 2>but you can't have two pointing to the same resource, so.

384
00:18:54.599 --> 00:18:56.519
<v Speaker 1>It enforces that single owner idea.

385
00:18:56.680 --> 00:18:59.400
<v Speaker 2>What else it handles a raise correctly if you use

386
00:18:59.440 --> 00:19:02.079
<v Speaker 2>unique toture, and you can provide custom deleters if the

387
00:19:02.119 --> 00:19:04.519
<v Speaker 2>resource isn't managed by a simple delete like closing a

388
00:19:04.519 --> 00:19:07.880
<v Speaker 2>file handle. It's generally your first choice for managing dynamic memory.

389
00:19:07.920 --> 00:19:11.279
<v Speaker 1>Okay, exclusive ownership, what if you need shared ownership?

390
00:19:11.359 --> 00:19:14.440
<v Speaker 2>That's where std dot shared GTR comes in. It's designed

391
00:19:14.440 --> 00:19:16.759
<v Speaker 2>for scenarios where multiple parts of your code need to

392
00:19:16.759 --> 00:19:19.319
<v Speaker 2>share ownership of a resource and the last owner is

393
00:19:19.359 --> 00:19:23.640
<v Speaker 2>not known a priori think complex data structures or maybe

394
00:19:23.680 --> 00:19:24.839
<v Speaker 2>asynchronous callbacks.

395
00:19:24.880 --> 00:19:25.759
<v Speaker 1>How does it manage that?

396
00:19:26.000 --> 00:19:29.519
<v Speaker 2>It uses reference counting internally. It keeps track of how

397
00:19:29.559 --> 00:19:32.599
<v Speaker 2>many shared ptr instances are currently pointing to the same

398
00:19:32.680 --> 00:19:36.240
<v Speaker 2>resource via a shared control block. When a shared ptr

399
00:19:36.319 --> 00:19:38.920
<v Speaker 2>is copied, the count goes up. When a shared PDR

400
00:19:39.000 --> 00:19:41.759
<v Speaker 2>is destroyed or reset, the count goes down. When the

401
00:19:41.839 --> 00:19:44.519
<v Speaker 2>count reaches zero, the resource is automatically deleted.

402
00:19:45.039 --> 00:19:47.400
<v Speaker 1>That sounds like it has more overhead than unique curtio.

403
00:19:47.559 --> 00:19:50.240
<v Speaker 2>It does size of shared ptrt is larger than size

404
00:19:50.240 --> 00:19:52.920
<v Speaker 2>of dqrt because it needs space for the pointer to

405
00:19:52.960 --> 00:19:55.680
<v Speaker 2>the resource and a pointer to the control block. There's

406
00:19:55.720 --> 00:20:00.000
<v Speaker 2>also the atomic overhead of incrementing and decrementing the reference count,

407
00:20:00.319 --> 00:20:03.039
<v Speaker 2>which matters in multi threaded code, so you use it

408
00:20:03.079 --> 00:20:06.279
<v Speaker 2>when you truly need shared ownership semantics.

409
00:20:05.759 --> 00:20:09.720
<v Speaker 1>And for creating shared PTRs, the book strongly recommends sdd

410
00:20:09.839 --> 00:20:12.599
<v Speaker 1>dot make shared. There's a performance reason for that, right

411
00:20:12.640 --> 00:20:13.279
<v Speaker 1>and a huh yeah.

412
00:20:13.519 --> 00:20:17.599
<v Speaker 2>Absolutely avoid doing shared ptrt. Always prefer std dot make

413
00:20:17.640 --> 00:20:21.279
<v Speaker 2>shared T. The AHA moment is this, make shared performs

414
00:20:21.279 --> 00:20:24.079
<v Speaker 2>a single memory allocation that holds both the object T

415
00:20:24.359 --> 00:20:25.680
<v Speaker 2>and its control block together.

416
00:20:25.960 --> 00:20:28.920
<v Speaker 1>Whereas new T followed by the shared ptr constructor does

417
00:20:28.920 --> 00:20:30.039
<v Speaker 1>two separate allocations.

418
00:20:30.240 --> 00:20:34.559
<v Speaker 2>It's exactly two allocations, means more overhead, potentially worse cash locality,

419
00:20:34.680 --> 00:20:37.079
<v Speaker 2>The object in its counter might be far apart in memory,

420
00:20:37.400 --> 00:20:40.960
<v Speaker 2>and critically a potential leak risk. Oh So imagine f

421
00:20:41.039 --> 00:20:45.480
<v Speaker 2>shared ptrkeg. If G throws an exception after new T succeeds,

422
00:20:45.880 --> 00:20:49.119
<v Speaker 2>but before the shared ptr constructor takes ownership, the newly

423
00:20:49.160 --> 00:20:52.920
<v Speaker 2>allocated T object is leaked Forever. Make shared avoids this

424
00:20:52.960 --> 00:20:56.039
<v Speaker 2>by doing the allocation and construction in a safer combined.

425
00:20:55.640 --> 00:20:59.680
<v Speaker 1>Step clever, Okay, one more smart cooiner std dot weakptr.

426
00:20:59.720 --> 00:21:02.119
<v Speaker 1>It's ones less useful, not.

427
00:21:02.079 --> 00:21:05.279
<v Speaker 2>Less useful, just different. Std dot weekptr is a companion

428
00:21:05.359 --> 00:21:08.200
<v Speaker 2>to std dot shared ptr. It holds a non owning

429
00:21:08.240 --> 00:21:10.960
<v Speaker 2>reference to an object managed by shared ptr. It observes

430
00:21:11.000 --> 00:21:13.359
<v Speaker 2>the object but doesn't affect its reference count. You can

431
00:21:13.400 --> 00:21:15.759
<v Speaker 2>try to lock a weekptr to get a temporary shared

432
00:21:15.839 --> 00:21:17.319
<v Speaker 2>ptr if the object still exists.

433
00:21:17.319 --> 00:21:18.240
<v Speaker 1>What's the main use case?

434
00:21:18.480 --> 00:21:22.279
<v Speaker 2>The absolute killer use case is breaking cycles. If you

435
00:21:22.279 --> 00:21:25.160
<v Speaker 2>have two objects that hold shared PDRs to each other,

436
00:21:25.519 --> 00:21:28.319
<v Speaker 2>they create a reference cycle. The reference counts will never

437
00:21:28.440 --> 00:21:31.680
<v Speaker 2>drop to zero even when all external shared PDRs are

438
00:21:31.720 --> 00:21:35.359
<v Speaker 2>gone memory leak. By having one object hold a weekpdr

439
00:21:35.400 --> 00:21:37.720
<v Speaker 2>to the other, you break the cycle, allowing them to

440
00:21:37.759 --> 00:21:41.359
<v Speaker 2>be destroyed correctly. It's essential for implementing caches or observer

441
00:21:41.400 --> 00:21:43.039
<v Speaker 2>patterns safely with shared.

442
00:21:42.799 --> 00:21:46.440
<v Speaker 1>Pdr right breaking those ownership loops. Okay, we've covered the

443
00:21:46.440 --> 00:21:50.680
<v Speaker 1>standard tools. Let's go beyond the standard customization performance overloading

444
00:21:50.680 --> 00:21:52.079
<v Speaker 1>memory allocation operators.

445
00:21:52.440 --> 00:21:56.119
<v Speaker 2>Yes, C plus plus gives you the power to completely

446
00:21:56.200 --> 00:22:01.079
<v Speaker 2>replace the default memory allocation functions. Operator new and operator delete.

447
00:22:01.319 --> 00:22:04.599
<v Speaker 2>You can provide global overrides or member function overrides for

448
00:22:04.640 --> 00:22:05.519
<v Speaker 2>specific classes.

449
00:22:05.559 --> 00:22:06.400
<v Speaker 1>Why would you do that?

450
00:22:06.559 --> 00:22:12.079
<v Speaker 2>Performance custom memory tracking using specialized allocators like pool allocators arenas.

451
00:22:12.359 --> 00:22:15.759
<v Speaker 2>But as the book says, great power comes great responsibility.

452
00:22:16.279 --> 00:22:19.880
<v Speaker 2>If you override these, you're the memory manager. Small mistates

453
00:22:19.920 --> 00:22:23.599
<v Speaker 2>can have a huge impact, causing leaks, corruption, or performance degradation.

454
00:22:23.880 --> 00:22:27.759
<v Speaker 1>Okay, so it's advanced stuff. What about placement new. The

455
00:22:27.759 --> 00:22:30.359
<v Speaker 1>book calls it the most important version of operator new.

456
00:22:30.440 --> 00:22:34.359
<v Speaker 2>Why because placement new doesn't actually allocate memory. Its signature

457
00:22:34.400 --> 00:22:39.359
<v Speaker 2>looks like void operator newstd dot size void ptr. It

458
00:22:39.400 --> 00:22:42.279
<v Speaker 2>takes a size and a pre allocated pointer ptr, and

459
00:22:42.319 --> 00:22:44.640
<v Speaker 2>it simply returns that same pointer ptr.

460
00:22:44.880 --> 00:22:45.960
<v Speaker 1>So what's the point.

461
00:22:46.519 --> 00:22:49.079
<v Speaker 2>Its point is to construct an object at a specific

462
00:22:49.160 --> 00:22:52.680
<v Speaker 2>memory location that you've already obtained. Somehow, it decouples allocation

463
00:22:52.759 --> 00:22:53.440
<v Speaker 2>from construction.

464
00:22:53.640 --> 00:22:54.640
<v Speaker 1>And where is that useful?

465
00:22:54.799 --> 00:22:58.279
<v Speaker 2>Several places mapping C plus plus objects directly onto memory.

466
00:22:58.319 --> 00:23:02.480
<v Speaker 2>MAPP hardware registers implementing custom containers that pre allocate large

467
00:23:02.559 --> 00:23:05.400
<v Speaker 2>chunks of memory and then use placement new to construct

468
00:23:05.480 --> 00:23:08.880
<v Speaker 2>objects within that chunk as needed, much faster than allocating

469
00:23:08.920 --> 00:23:12.960
<v Speaker 2>each object individually. And it's key for optimizations like small

470
00:23:12.960 --> 00:23:17.960
<v Speaker 2>object optimization SOO or small string optimization SSO. In std

471
00:23:18.039 --> 00:23:20.960
<v Speaker 2>dot function or std dot string, they might use internal

472
00:23:21.000 --> 00:23:24.000
<v Speaker 2>buffer space and placement new for small object strings, avoiding

473
00:23:24.039 --> 00:23:25.200
<v Speaker 2>heap allocation entirely.

474
00:23:25.480 --> 00:23:29.240
<v Speaker 1>That makes sense, decoupling allocation and construction. The book even

475
00:23:29.319 --> 00:23:33.799
<v Speaker 1>walks through writing a simple leak detector using operator overloading.

476
00:23:33.880 --> 00:23:36.799
<v Speaker 2>Yes, a great practical example. Yeah, you overload global new

477
00:23:36.839 --> 00:23:40.000
<v Speaker 2>and delete. In new, you allocate a bit extra memory,

478
00:23:40.519 --> 00:23:43.160
<v Speaker 2>store the requested size and that extra bit at the beginning,

479
00:23:43.519 --> 00:23:46.559
<v Speaker 2>and return a pointer just past that hidden size info.

480
00:23:46.720 --> 00:23:49.799
<v Speaker 1>That's the lie it mentions, hiding the size exactly.

481
00:23:50.160 --> 00:23:53.200
<v Speaker 2>Then in your overloaded delead you take the user's pointer

482
00:23:53.640 --> 00:23:56.559
<v Speaker 2>back up slightly to read the hidden size, record that

483
00:23:56.640 --> 00:24:00.000
<v Speaker 2>this block is now free, and then deallocate the entire blode,

484
00:24:00.440 --> 00:24:04.200
<v Speaker 2>including the hidden size. You use some central accountant object

485
00:24:04.240 --> 00:24:06.200
<v Speaker 2>to track what's allocated versus.

486
00:24:05.880 --> 00:24:09.160
<v Speaker 1>Freed, and it uses the Meyer's singleton for that accountant.

487
00:24:09.200 --> 00:24:11.720
<v Speaker 1>What's the catch there and the alignment issue?

488
00:24:11.920 --> 00:24:15.200
<v Speaker 2>Right? The Meyer singleton uses a static local variable inside

489
00:24:15.200 --> 00:24:18.119
<v Speaker 2>of function to ensure only one instance of the accountant

490
00:24:18.200 --> 00:24:20.559
<v Speaker 2>is created, and it's done in a thread safe way.

491
00:24:20.680 --> 00:24:23.920
<v Speaker 2>In modern c plus plus thanks to magic statics doing

492
00:24:23.960 --> 00:24:27.839
<v Speaker 2>implicit syncretization. The catch is that this synchronization can add

493
00:24:27.880 --> 00:24:32.119
<v Speaker 2>a small performance overhead. The critical pitfall, though, is alignment.

494
00:24:32.759 --> 00:24:35.160
<v Speaker 2>If you just store size it for the size, what

495
00:24:35.200 --> 00:24:38.519
<v Speaker 2>if the object being allocated needs stricter alignment than size it,

496
00:24:38.720 --> 00:24:41.720
<v Speaker 2>like an object needing sixteen by alignment, Your hidden size

497
00:24:41.799 --> 00:24:44.519
<v Speaker 2>might throw off the alignment of the object itself.

498
00:24:44.279 --> 00:24:46.720
<v Speaker 1>Out serious trouble. How do you fix that?

499
00:24:46.799 --> 00:24:49.480
<v Speaker 2>The fix is clever. You allocate enough space at the start,

500
00:24:49.559 --> 00:24:52.279
<v Speaker 2>not just for size it, but for std do maxiligned

501
00:24:52.839 --> 00:24:56.440
<v Speaker 2>This type guarantees alignment suitable for any standard type. You

502
00:24:56.480 --> 00:24:59.440
<v Speaker 2>store your size within that maxiline space and then return

503
00:24:59.480 --> 00:25:03.079
<v Speaker 2>the pointer. After that guaranteed alignment boundary, you jump over

504
00:25:03.119 --> 00:25:06.519
<v Speaker 2>the metadata space, ensuring the user's object is correctly aligned.

505
00:25:06.640 --> 00:25:10.160
<v Speaker 1>Very neat, okay, moving faster now. Arena based memory management

506
00:25:10.200 --> 00:25:10.799
<v Speaker 1>sounds fast.

507
00:25:11.000 --> 00:25:14.640
<v Speaker 2>It could be blazingly fast. The idea is simple. Allocate

508
00:25:14.720 --> 00:25:18.400
<v Speaker 2>one huge chunk of memory up front the arena. Then

509
00:25:18.839 --> 00:25:21.799
<v Speaker 2>your custom operator new just bumps a pointer within that

510
00:25:21.880 --> 00:25:25.000
<v Speaker 2>arena to hand out memory. A delete might do nothing

511
00:25:25.000 --> 00:25:28.119
<v Speaker 2>at all. Nothing Often Yeah, you just use the arena

512
00:25:28.200 --> 00:25:31.759
<v Speaker 2>for a specific phase like loading a game level, processing

513
00:25:31.759 --> 00:25:34.720
<v Speaker 2>a request, and when you're done, you discard the entire

514
00:25:34.759 --> 00:25:38.880
<v Speaker 2>arena in one go. Allocation becomes just pointer edition deallocation

515
00:25:38.920 --> 00:25:42.480
<v Speaker 2>becomes a no op until the end. Super fast, minimal

516
00:25:42.519 --> 00:25:43.839
<v Speaker 2>fragmentation within the arena.

517
00:25:44.000 --> 00:25:44.839
<v Speaker 1>Where is this used?

518
00:25:45.079 --> 00:25:47.880
<v Speaker 2>Games are a classic example. Memory po level or seam

519
00:25:48.160 --> 00:25:52.440
<v Speaker 2>high performance servers, memory per request compilers anywhere you have

520
00:25:52.480 --> 00:25:55.400
<v Speaker 2>predictable phases of allocation and deallocation.

521
00:25:54.880 --> 00:25:56.559
<v Speaker 1>And the performance numbers are impressive.

522
00:25:56.599 --> 00:25:59.319
<v Speaker 2>The book site's examples showing custom new taking maybe seventy

523
00:25:59.319 --> 00:26:01.240
<v Speaker 2>five percent of the time I'm a standard new, and

524
00:26:01.240 --> 00:26:03.680
<v Speaker 2>custom delete being a tiny fraction like eight percent of

525
00:26:03.720 --> 00:26:05.799
<v Speaker 2>the time of standard delete because it often does nothing.

526
00:26:06.319 --> 00:26:08.200
<v Speaker 2>Huge gains possible if the pattern.

527
00:26:07.880 --> 00:26:10.160
<v Speaker 1>Fits and chunked pools are related.

528
00:26:10.000 --> 00:26:13.160
<v Speaker 2>Kind of an extension. Instead of one big arena for anything,

529
00:26:13.240 --> 00:26:16.119
<v Speaker 2>you might have multiple pools, each managing fixed sized chunks

530
00:26:16.160 --> 00:26:19.319
<v Speaker 2>of memory. Eg. A pool for thirty two byte objects,

531
00:26:19.359 --> 00:26:22.079
<v Speaker 2>one for sixty four byte, et cetera. When you need

532
00:26:22.079 --> 00:26:24.359
<v Speaker 2>an object of a certain size, you grab a pre

533
00:26:24.400 --> 00:26:28.319
<v Speaker 2>allocated block from the corresponding pool. Very efficient for allocating

534
00:26:28.319 --> 00:26:30.079
<v Speaker 2>many objects of a few common sizes.

535
00:26:30.200 --> 00:26:33.079
<v Speaker 1>Okay, powerful stuff. Now let's look at the bleeding edge.

536
00:26:33.480 --> 00:26:36.960
<v Speaker 1>C plus plus keeps evolving. C plus plus twenty three

537
00:26:37.079 --> 00:26:39.400
<v Speaker 1>just landed. C plus plus twenty six, C plus plus

538
00:26:39.440 --> 00:26:41.920
<v Speaker 1>twenty nine are coming. What's new in memory management?

539
00:26:42.000 --> 00:26:45.039
<v Speaker 2>Right? The language doesn't stand still. These newer features often

540
00:26:45.079 --> 00:26:48.960
<v Speaker 2>address long standing tricky problems or enable new optimizations. A

541
00:26:48.960 --> 00:26:52.000
<v Speaker 2>big one in C plus plus twenty three is std

542
00:26:52.119 --> 00:26:52.799
<v Speaker 2>dot start.

543
00:26:52.599 --> 00:26:55.200
<v Speaker 1>Lifetimess what problem needed solving there.

544
00:26:55.400 --> 00:26:57.680
<v Speaker 2>Imagine reading raw bytes from a network socket or a

545
00:26:57.720 --> 00:27:00.720
<v Speaker 2>file into a SHAR buffer or std dot com byte buffer.

546
00:27:00.960 --> 00:27:04.359
<v Speaker 2>You know those bytes represents a my packet strucked, but

547
00:27:04.440 --> 00:27:08.480
<v Speaker 2>the C plus plus compiler doesn't. Officially no, mypacket object's

548
00:27:08.519 --> 00:27:10.440
<v Speaker 2>lifetime has begun in that buffer.

549
00:27:10.079 --> 00:27:12.440
<v Speaker 1>So just casting the buffer pointer to my packet is.

550
00:27:12.920 --> 00:27:16.400
<v Speaker 2>Technically undefined behavior. It might seem to work by luck

551
00:27:16.400 --> 00:27:20.279
<v Speaker 2>on your compiler, but it's not guaranteed or portable. STD

552
00:27:20.400 --> 00:27:24.400
<v Speaker 2>dot start lifetime is my packet buffer ptr is the fix.

553
00:27:24.960 --> 00:27:28.839
<v Speaker 2>It's like a magical no op function that doesn't generate code.

554
00:27:28.839 --> 00:27:31.960
<v Speaker 2>What tells the compiler trust me, the lifetime of a

555
00:27:32.039 --> 00:27:35.519
<v Speaker 2>mypacket object has now implicitly started at this location.

556
00:27:35.799 --> 00:27:38.119
<v Speaker 1>AH, so it makes it common. Pattern is safe and

557
00:27:38.160 --> 00:27:40.960
<v Speaker 1>portable that sounds like a huge relief for network programmers.

558
00:27:41.039 --> 00:27:43.880
<v Speaker 2>Absolutely, the book calls it a potential boon to network

559
00:27:43.880 --> 00:27:47.839
<v Speaker 2>programmers everywhere and anyone reading binary data. It closes a

560
00:27:47.880 --> 00:27:49.559
<v Speaker 2>long standing ub loople.

561
00:27:49.440 --> 00:27:52.480
<v Speaker 1>Okay, C plus plus twenty six mentions. Trivial relocation that

562
00:27:52.519 --> 00:27:53.519
<v Speaker 1>sounds like an optimization.

563
00:27:53.880 --> 00:27:57.720
<v Speaker 2>It's a potentially huge optimization. Normally, moving an object means

564
00:27:57.720 --> 00:28:00.839
<v Speaker 2>copy constructing it at the destination and then destructing it

565
00:28:00.880 --> 00:28:03.920
<v Speaker 2>at the source. Even with move semantics, there's still a

566
00:28:03.960 --> 00:28:07.839
<v Speaker 2>copy of members and a destroy. Trivial relocation applies to

567
00:28:07.880 --> 00:28:10.000
<v Speaker 2>types where moving them is equivalent to just doing a

568
00:28:10.039 --> 00:28:13.319
<v Speaker 2>mebcapie of their bytes and leaving the source memory uninitialized.

569
00:28:13.759 --> 00:28:16.599
<v Speaker 2>No constructor or destructor calls needed for the move itself.

570
00:28:16.799 --> 00:28:20.200
<v Speaker 1>So for types like std dot vectorant, maybe moving it

571
00:28:20.240 --> 00:28:23.200
<v Speaker 1>could just copy the pointer size and capacity bytes.

572
00:28:23.279 --> 00:28:27.160
<v Speaker 2>Potentially Yes, If a type like maybe std dot uniquipdr

573
00:28:27.359 --> 00:28:30.680
<v Speaker 2>or simple pod like structs or potentially containers under the

574
00:28:30.759 --> 00:28:35.440
<v Speaker 2>right conditions is deemed trivially relocatable, the compiler can optimize

575
00:28:35.440 --> 00:28:37.079
<v Speaker 2>moves down to a raw memory.

576
00:28:36.759 --> 00:28:38.039
<v Speaker 1>Copy and the bed part.

577
00:28:38.119 --> 00:28:41.000
<v Speaker 2>The best part, as the book highlights, is that existing

578
00:28:41.039 --> 00:28:43.960
<v Speaker 2>code using std dot mob on these types compiled with

579
00:28:44.079 --> 00:28:47.359
<v Speaker 2>previous standards could just run faster without changing a single

580
00:28:47.400 --> 00:28:50.039
<v Speaker 2>line of source code when recompiled with a C plus

581
00:28:50.079 --> 00:28:53.160
<v Speaker 2>plus twenty six compiler that implements this free performance.

582
00:28:53.279 --> 00:28:56.319
<v Speaker 1>Nice okay, last one type of ware allocation and de

583
00:28:56.480 --> 00:28:58.519
<v Speaker 1>allocation functions. What's this about?

584
00:28:58.720 --> 00:29:01.880
<v Speaker 2>This is a really interesting propose. It allows overloading operator

585
00:29:01.960 --> 00:29:05.240
<v Speaker 2>new and operator delete to take an extra argument STD

586
00:29:05.440 --> 00:29:07.119
<v Speaker 2>dot type identity T, so.

587
00:29:07.039 --> 00:29:09.920
<v Speaker 1>The allocator function itself knows the type of the object

588
00:29:09.960 --> 00:29:11.599
<v Speaker 1>being allocated exactly.

589
00:29:12.079 --> 00:29:15.720
<v Speaker 2>Currently, global operator new just gets a size request. It

590
00:29:15.759 --> 00:29:18.480
<v Speaker 2>doesn't know if you're allocating an int, a STD dot string,

591
00:29:18.880 --> 00:29:22.359
<v Speaker 2>or a huge custom class. With type awareness, the allocation

592
00:29:22.440 --> 00:29:25.400
<v Speaker 2>function could potentially use different strategies based on the type T.

593
00:29:25.880 --> 00:29:29.319
<v Speaker 2>Maybe allocate small objects from one pool, large objects from another,

594
00:29:29.680 --> 00:29:32.119
<v Speaker 2>or objects of a specific class known to benefit from

595
00:29:32.119 --> 00:29:35.000
<v Speaker 2>a certain memory layout from a dedicated arena.

596
00:29:35.079 --> 00:29:37.359
<v Speaker 1>So it could integrate some of those custom arena pool

597
00:29:37.359 --> 00:29:41.039
<v Speaker 1>benefits directly into the global allocation mechanism guided by type.

598
00:29:41.119 --> 00:29:44.160
<v Speaker 2>That's the idea. It opens up new avenues for optimizations

599
00:29:44.200 --> 00:29:48.680
<v Speaker 2>based on type information, potentially making customized allocation strategies easier

600
00:29:48.720 --> 00:29:51.519
<v Speaker 2>to implement and integrate system wide. It's a really forward

601
00:29:51.519 --> 00:29:52.200
<v Speaker 2>looking concept.

602
00:29:52.279 --> 00:29:55.680
<v Speaker 1>Wow. Okay, we've really gone from the absolute fundamentals like

603
00:29:55.720 --> 00:29:57.720
<v Speaker 1>an int being an object, all the way through the

604
00:29:57.720 --> 00:30:03.000
<v Speaker 1>weeds of UGB, the elegance OFRIII and smart pointers, custom allocators,

605
00:30:03.079 --> 00:30:05.319
<v Speaker 1>and now these cutting edge C plus plus twenty three

606
00:30:05.359 --> 00:30:08.720
<v Speaker 1>twenty six features. It's amazing how much depth there is

607
00:30:08.759 --> 00:30:12.119
<v Speaker 1>to C plus plus memory management and how Patresea's book

608
00:30:12.200 --> 00:30:13.759
<v Speaker 1>lays it all out. It really gives you a sense

609
00:30:13.799 --> 00:30:15.880
<v Speaker 1>of how the software connects to the actual machine.

610
00:30:16.000 --> 00:30:18.519
<v Speaker 2>It really does. Yeah, and understating this stuff, even if

611
00:30:18.519 --> 00:30:22.000
<v Speaker 2>you don't write C plus plus every day, it's incredibly valuable.

612
00:30:22.319 --> 00:30:27.200
<v Speaker 2>These concepts ownership, resource, lifetime, careful memory handling. They echo

613
00:30:27.319 --> 00:30:30.119
<v Speaker 2>in many other languages and systems. It empowers you to

614
00:30:30.119 --> 00:30:34.519
<v Speaker 2>write code that's more robust, definitely more efficient, and ultimately safer.

615
00:30:34.920 --> 00:30:37.839
<v Speaker 1>Absolutely, it makes you think differently about how programs work.

616
00:30:38.319 --> 00:30:40.880
<v Speaker 1>So here's a final thought to leave you with. Considering

617
00:30:40.880 --> 00:30:44.240
<v Speaker 1>this constant evolution we've just discussed, especially in C plus

618
00:30:44.240 --> 00:30:47.319
<v Speaker 1>plus pilation, how might the whole concept of ownership in

619
00:30:47.359 --> 00:30:51.000
<v Speaker 1>programming languages keep changing in the coming years. What new

620
00:30:51.039 --> 00:30:53.400
<v Speaker 1>abstractions might we see built on top of these low

621
00:30:53.480 --> 00:30:55.559
<v Speaker 1>level memory foundations we've explored today,
