C++ Memory Management: Write leaner and safer C++ code using proven memory-management techniques - podcast episode cover

C++ Memory Management: Write leaner and safer C++ code using proven memory-management techniques

Aug 19, 202531 min
--:--
--:--
Download Metacast podcast app
Listen to this episode in Metacast mobile app
Don't just listen to podcasts. Learn from them with transcripts, summaries, and chapters for every episode. Skim, search, and bookmark insights. Learn more

Episode description

Provides an extensive overview of C++ memory management, covering both fundamental concepts and advanced techniques. It begins by explaining core ideas like objects, pointers, references, object lifetime, and the dangers of undefined behavior. The text then progresses to resource management using destructors and the RAII idiom, followed by a detailed exploration of smart pointers, including std::unique_ptr, std::shared_ptr, and std::weak_ptr. Furthermore, it examines overloading memory allocation operators such as new and delete, atypical allocation mechanisms, and arena-based memory management for performance optimization. Finally, the source discusses writing generic containers with both explicit and implicit memory management, allocator support, and contemporary C++ features related to memory handling.

You can listen and download our episodes for free on more than 10 different platforms:
https://linktr.ee/cyber_security_summary

Get the Book now from Amazon:
https://www.amazon.com/Memory-Management-leaner-memory-management-techniques/dp/1805129805?&linkCode=ll1&tag=cvthunderx-20&linkId=8c5b3a91d4f4dd60871563eeede8bbaf&language=en_US&ref_=as_li_ss_tl


Discover our free courses in tech and cybersecurity, Start learning today:
https://linktr.ee/cybercode_academy

Transcript

Speaker 1

Okay, let's unpack this. Imagine your software running right, doing its thing, but underneath all that, there's this this hidden world, unseen forces basically running the show. It's the connection between your code and the computer's memory. Today we're doing a deep dive into C plus plus memory management. It's fascinating stuff, but yeah, often pretty intimidating too. We're using Patresea's really

comprehensive book as our guide here. Now. C plus plus memory Management it can feel like a maze, you know, full of complex rules, hidden traps. But our mission today is to give you a shortcut. We want to boil down the core ideas, point out those pitfalls, and look at the modern ways of doing things. Hopefully help you write C plus plus code that's better, safer, more expressive. Expect some uh aha moments today.

Speaker 2

Absolutely becres he has this great skill, doesn't he, Taking these really dense technical topics and making them feel well, almost conversational. This book, it's not just rules. It really explores how C plus plus talks to memory at its heart. It's quite empowering.

Speaker 1

Actually totally agree. So let's start right at the beginning, the building blocks. What's the absolute most fundamental thing we need to get our heads around with C plus plus memory.

Speaker 2

Well, what's really interesting is how C plus plus even defines an object. You know, most people think classes.

Speaker 1

Strucks, right, yeah, that's what I think.

Speaker 2

But in C plus plus AC it's actually much broader. Even basic types like an int or a float they're considered objects too.

Speaker 1

Wait, really an int is an object? How does that work?

Speaker 2

It's about having certain properties to be an object. In C plus plus FAG, something needs an address in memory, it needs to take up some space non zero storage that has a lifetime, maybe a name, definitely a type, and a storage duration. In fact, you can actually write static assert sdd dot ice object vint and well it'll compile just fine. Huh.

Speaker 1

Okay, so even a simple nt has all these characteristics. That's foundational.

Speaker 2

It really is understanding that helps make sense of a lot of other things.

Speaker 1

Okay, so everything has its place, it's time and memory. Speaking of places, pointers they're everywhere in C plus plus C right, even if the book mentions kind of usingly that a strict definition is surprisingly hard to come by in the standard.

Speaker 2

Exactly. Pointers are incredibly powerful tools, but that power comes with risk. Put it arithmetic for example, you can write it q qles P plus three, no problem. But if P isn't pointing where you think it is, trouble, big trouble. Q could point to some arbitrary location. You could cause serious damage. And the responsibility for managing that power it always falls back on the C plus plus programmer. Always right.

Speaker 1

With great power, it comes great responsibility. As they say, okay, what about void? I see that sometimes in like lower level code.

Speaker 2

Ah void. Think of it as just a raw memory address. It tells you where something is, but not what it is. It has no type information attached. Any other pointer type can convert to void, but going back the other way usually needs an explicit cast, basically saying here's a location, but I make no promises about what's there.

Speaker 1

Got it. So beyond just what an object is, let's talk about its life in memory, lifetime, size, alignment padding. How does C plus plus manage lifetime?

Speaker 2

Lifetime is absolutely key. Automatic objects, the ones usually on the stack, have very predictable, deterministic lifetimes. They get created, they go out of scope, Boom, they're destructed. Static objects live for pretty much the whole program run. But dynamically allocated objects, the ones you create with new. Their lifetime is well when your program says so, and that's where things can go wrong.

Speaker 1

Ah okay, So if it's when your program says so, I'm guessing the classic mistake is forgetting to say it's over right, forgetting to delete.

Speaker 2

That's the number one pitfall. Absolutely, you call new, you get your object, it's constructor runs. But if you forget delete, the destructor never runs, and the memory it just leaks away. Resource leak, memory leak, bad news.

Speaker 1

Right. Okay, Now this next bit sounds really interesting. Size alignment and padding using size isn't always straightforward.

Speaker 2

No, so it often surprises people. This is a great aha moment. Let's take a simple class class X charcis short S and tn.

Speaker 1

Okay, a char a short an int, So like one by plus two bytes plus four bytes seven bytes.

Speaker 2

That's what you'd intuitively think, right, But if you actually check size of x, you'll probably find it's eight bytes eight.

Speaker 1

Where does the extra byte come from?

Speaker 2

Comes from? Padding bytes? Compilers often add these invisible bytes between members, So between C and S there might be a padding byte between S and N maybe two padding bytes. Why performance exactly? Performance CPU's access data much more efficiently when it's aligned on certain boundaries, like a four byte boundary for an ind So the compiler adds padding to ensure each member starts at an optimal address.

Speaker 1

Okay, that makes sense, But the book also mentions trailing padding bytes patting at the end of the object.

Speaker 2

Why ah, that's specifically for arrays of these objects. Remember, array elements have to sit right next to each other in memory contiguously. That trailing padding ensures that if array is properly aligned, then R one will also start on the correct alignment boundary, and so on. It's subtle, but crucial for array correctness and performance.

Speaker 1

Wow. Okay, so memory isn't quite as packed as you might think. This has got to affect how C plus plus copies and moves objects around.

Speaker 2

Surely, Oh, absolutely, Copy and move operations constructors assignment operators are deeply affected by this, especially when you write your own classes. For a really simple struct maybe like point two d just two floats, the compiler generated copy and move are usually fine, But if you have a class like say naive string, that manages its own raw memory like a character array, then the defaults are not. Okay, you absolutely need to write your own destructor copy constructor

copy assignment. That's the classic rule of three and.

Speaker 1

The rule of five. If you add move operations, which is pretty standard now in modern C plus.

Speaker 2

Plus, precisely you need custom logic to handle copying or moving that raw memory correctly, Otherwise you end up with shallow copies, double deletes all sorts of problems.

Speaker 1

And the book mentions s TD dot exchange as a helper for writing move constructors.

Speaker 2

Yeah, std dot exchange is neat little utility. It basically lets you swap the resource pointer in your move constructor. More cleanly, it assigns a new value like null ptr to the source objects pointer and gives you back the old value the resource pointer, all in one go. Makes the code a bit tidier less error prone.

Speaker 1

Okay, and while we're on data layout, what about basic built in arrays raw rays? They're just sequences in memory, right, and pointer arithmetic is key there too, Exactly.

Speaker 2

Raw rays are contiguous sequences, and accessing RA is basically the same as writing R plus I. The crucial thing is that the plus i isn't just adding ie bytes. It's typed. It means add i times the size of one element. So if it's an array events, it adds i size of bytes. That's how it steps correctly from one element to the next.

Speaker 1

Right, it knows the size of the things in the array precisely.

Speaker 2

Oh. In an interesting side note, new and darro is actually allowed, you can dynamically allocate a zero sized array. Doesn't sound useful, but it can simple some generic code sometimes.

Speaker 1

Okay, we've got the building blocks. Now let's walk down what the book calls the perilous path things to be really careful with. It talks about different kinds of evil in C plus plus. What let's start with. IFNDR sounds ominous.

Speaker 2

IFNDR ill formed no diagnostic required, it's insidious. It means your code is technically broken according to the C plus plus standard, but the compiler isn't actually required to warn you about it.

Speaker 1

So it might compile fine, but just not work, or work sometimes.

Speaker 2

Exactly silent failures really hard to debug. The compiler might not have enough info compile time, or the standard just gives it a pass on issuing a diagnostic. Very dangerous, okay.

Speaker 1

And then the big one, undefined behavior UB everyone talks about ub ah.

Speaker 2

Yes, UB undefined behavior means the C plus plus standard places absolutely no requirement on what happens if your code triggers it. Anything could happen. It might crash, it might give the wrong answer, it might format your hard drive. Okay, probably not that last one, but technically the standard doesn't forbid it.

Speaker 1

So like dear referencing a null pointer or using an uninitialized variable, those are.

Speaker 2

The classic examples. Yes, dear referencing null, using uninitialized memory, accessing a raise out of bounds, signed integer overflow. The list goes on. If you hit UB, you are in serious trouble. As the book puts it, all bets are off.

Speaker 1

And the scary part is how compilers treat it. Right, they optimize around it.

Speaker 2

That's the really unsettling part. Compilers assume that correct C plus plus code never contains UB, so if they see potentially UB, they might just optimize assuming it can't happen. They might remove null checks because hey, you wouldn't dare reference null later, right. They might reorder operations or eliminate entire loops based on assumptions flowing from potentially UB. It leads to sometimes surprising effects.

Speaker 1

To put it mildly, wow, So UB isn't just It might crash. It's the compiler might silently break my code in weird ways.

Speaker 2

Precisely treat you b as a critical bug. Always don't rely on it working on your specific compiler version.

Speaker 1

Okay, IFNDR, what about the one definition rule the ODR. That sounds important, but maybe less evil.

Speaker 2

The ODR is crucial, but yeah, maybe less overtly evil, more subtly problematic if violated. It basically says that anything in your program, a function, a class, a template, a global variable must have exactly one definition in the entire program.

Speaker 1

And if you have two definitions, say in different.

Speaker 2

Files, then you're often in IFNDR territory again, or the linker might just silently pick one definition and ignore the other. This can lead to absolute chaos, especially with templates or inline functions defined slightly differently in headers included in different places, very hard to track down bugs.

Speaker 1

And implementation defined behavior. How does that fit in?

Speaker 2

That's different? Implementation defined means the standard allows different behaviors, but requires the compiler vendor to document exactly what their implementation does.

Speaker 1

So it's predictable on one compiler, but maybe not portable.

Speaker 2

Exactly, Like the size of NT or the result of right fifting a negative number. It's not inherently evil, just something to be aware of if you need your code to work identically everywhere.

Speaker 1

Okay, moving on to specific dangerous things point or maneuvers beyond just adding random numbers. The book mentions type punning.

Speaker 2

What's that type punning? It's essentially trying to subvert the type system. As the book says, you have memory representing one type, say a float, and you try to access it as if it were a different type, say an int, to reinterpret its raw bits.

Speaker 1

How do people usually try to do this?

Speaker 2

A common but usually illegal way in C plus plus plus S is using a union. You have a union with maybe float F and an N you're at to UDAF. Then you immediately try to read you in.

Speaker 1

And that's ub even though they share the same memory.

Speaker 2

Generally, yes, that's UBN C plus plus here. Reading from a union member that wasn't the last one written to is undefined unless you're dealing with what's called the common initial sequence.

Speaker 1

Common initial sequence.

Speaker 2

Yeah, it's a narrow exception. If two structs or classes in the union start with the same sequence of men types like both start with an innt followed by a char. You can write to one and then read that common sequence part through the other, but it's complex. Relying on union penning is usually asking for trouble.

Speaker 1

What about using std dot memppi. I've seen that used for reinterpreting.

Speaker 2

Bits, right, mechpapie is often suggested. It can be used, but you have to be careful. If you mempi the bytes of a float into some raw memory buffer and then immediately try to read that buffer using an INT, that's still you b why because mechpi doesn't magically start the lifetime of nnt object there. It might start the lifetime of a float if the conditions are right, but accessing it via ND violates type rules.

Speaker 1

So how do you use megpi correctly for this?

Speaker 2

The safe way is to magpie the bytes into an already existing object of the target type, so you'd have an invariable already and you may compile the float's bites into the memory location of that INT. That's generally okay.

Speaker 1

This is getting really low level. Are there specific types for messing with addresses as numbers?

Speaker 2

Yes, C plus plus provides std dot ntptr t and ftd dot untptr. These are integer types guaranteed to be large enough to hold a pointer value. You can cast a pointer to one of these, do some integer math, maybe, and then cast it back, But the book warns they are inherently fragile and non portable. Things like bait order indianness can bite you. If you're storing addresses as integers, you're really operating outside the safety of the type system.

Use with extreme caution, usually only needed for very specific, low level tasks.

Speaker 1

Right, Okay, we've seen the dangers. Let's talk about tools for control. Casting spells as the book calls them. How should we think about casts? Are we lying to the compiler?

Speaker 2

That's a common misconception. Yeah, It's better to think of casts not as lying, but as adjusting the compiler's view. You're explicitly telling the compiler. I know you see this expression as type A, but I want you to treat it as type B for this specific operation. You're signaling your intent.

Speaker 1

Okay, makes sense. Let's go through the C plus plus name casts. First. Stato cast. The book calls it our best friend.

Speaker 2

And for good reason. Static cast is for well defined or reasonably safe conversions things the compiler could often do implicitly or standard conversions between related types like in to float or importantly, casting a pointer from a drive class to its public base class drive it base.

Speaker 1

Does stato caast ever change the actual memory address?

Speaker 2

Yes, it can. That's a key point. Ye. If use static cast to drive to a base in a multiple inheritance scenario, the address might actually change. The base subobject might not be located at the very beginning of the drived object and memory. Stata cast handles that address adjustment correctly.

Speaker 1

Okay, next dynamic cast, the book says it's assigned. Something's wrong. That's pretty strong.

Speaker 2

It often is assigned. Yeah. Yeah. Dynamic cast is used for navigating class hierarchies at runtime, typically casting down from a bates class point of reference to a drive class point of reference. It needs run time type information RTTI enabled, which adds overhead, and if the cast fails because the object isn't actually of the target drive type, it returns null ptr for pointers or throws std dot bad cast for references.

Speaker 1

So why is it assigned? Something's wrong?

Speaker 2

Because heavy reliance on dynamic cast often suggests your class design might be flawed. You might be better off using virtual functions polymorphism to achieve the desired behavior without needing to know the exact drived type at runtime. The book suggests its use implies a potentially perfectible interface.

Speaker 1

Got it, then const cast playing tricks with safety.

Speaker 2

Yeah. Constcast is the only cast that can remove cost or volatile. Its main legitimate use is often dealing with older APIs or libraries that weren't const correct. You might have a const object but need to pass it to a function that inexplicably takes a non constant pointer, even though you know the function won't actually modify it. But

it's dangerous, extremely dangerous if misused. If the object you're casting away consts from was originally declared as cost, then actually trying to modify it through the constcast at pointer is undefined behavior. You're breaking a fundamental primt use it very, very sparingly.

Speaker 1

And finally, the most feared one reinterpret cast. Believe me a compiler.

Speaker 2

That sums it up perfectly. Reinterpret cast does a raw bit level reinterpretation between pointer types or pointers and integers. It tails a compiler forget types. Just treat these bits as if they were this other pointer type m key danger. It does no address adjustments. Remember how static cast might adjust the address for base classes. Reinterpret cast doesn't, so.

Speaker 1

If you reinterpret cast a derived to a base in multiple inheritance, you'll.

Speaker 2

Likely get the wrong addressed for the base subobject, and trying to use that pointer leads straight to undefined behavior. It's incredibly powerful for low level bit manipulation, but incredibly easy to misuse with disastrous consequences. Only use it if you absolutely know what you're doing at the bit level.

Speaker 1

Any other is worth mentioning quickly.

Speaker 2

The book also notes STD dot bitcast from C plus plus twenty, which is a safer way to do the type punting reinterpretation for object types copying bits safely, and STD do duration cast for working with chronotime durations, which is very useful and safe.

Speaker 1

Okay, And the last cast, the reviled one, the C style cast TX a prick. Why is it so bad in C plus.

Speaker 2

Plus h several reasons. One, it's hard to search foreign coode compared to the name C plus plus casts, two, and most importantly, it lacks intent. A C style cast tries to be smart and will attempt to sequence of conversions, often ending up doing a stata cast, a costcast, or even a reinterpret cast, depending on the types involved.

Speaker 1

So am I do a dangerous reinterpret cast when you didn't explicitly mean.

Speaker 2

It to exactly? It hides the potential danger. Using the specific C plus plus casts forced you to think about what kind of conversion you actually need and makes your intent clear to other readers and your future self. Avoid C style casts in modern C.

Speaker 1

Plus plus right clear intent is key. Okay, we've seen the tools the dangers. Now let's talk about taking responsibility C plus plus style RAII and smart pointers. The book really emphasizes the power of destructors here.

Speaker 2

Destructors are arguably the killer feature of C plus plus for resource management. They enable RAII. Resource acquisition is initialization, though as the book chekily points out, the name is a bit misleading. It's really more to do with destructors than constructors. How So, the core idea is you acquire a resource like memory, a file handle, a lock in

an object's constructor. That object now owns the resource. When the object's light time ends, usually when it goes out of scope, its destructor is automatically called, and the destructor's job is to release the resource.

Speaker 1

Ah. So acquisition is tied to object initialization, but the magic happens at destruction.

Speaker 2

Precisely. This automatic cleanup is why some C plus plus luminaries, as the book calls them, say, the most beautiful instruction in C plus plus is the closing brace that marks the end of a scope triggering destructors.

Speaker 1

Because it guarantees cleanup even if errors happen exactly.

Speaker 2

RAII makes code naturally exception safe. If an exception is thrown, the stack on wines, scopes are exited, and the destructors of local objects are still called, ensuring resources aren't leaked. It's incredibly powerful and robust.

Speaker 1

This leads perfectly into smart pointers, doesn't it. Raw pointers, as we said, don't convey ownership clearly who's responsible for the delete.

Speaker 2

That's the fundamental problem raw pointers pose for resource management. Smart pointers solve this by encoding ownership semantics directly into the type system. They wrap a raw pointer and manage its lifetime according to specific rules.

Speaker 1

Let's start with std dot unique ftr std.

Speaker 2

Dot UNIQFTR represents exclusive ownership. It's the single clear last user of a resource. Only one unique FTR can own a particular object at any time. It's very lightweight, typically the same size as a raw pointer. Zero runtime overhead Crucially, it's noncopyable. You can move ownership from one uniquapture to another, but you can't have two pointing to the same resource, so.

Speaker 1

It enforces that single owner idea.

Speaker 2

What else it handles a raise correctly if you use unique toture, and you can provide custom deleters if the resource isn't managed by a simple delete like closing a file handle. It's generally your first choice for managing dynamic memory.

Speaker 1

Okay, exclusive ownership, what if you need shared ownership?

Speaker 2

That's where std dot shared GTR comes in. It's designed for scenarios where multiple parts of your code need to share ownership of a resource and the last owner is not known a priori think complex data structures or maybe asynchronous callbacks.

Speaker 1

How does it manage that?

Speaker 2

It uses reference counting internally. It keeps track of how many shared ptr instances are currently pointing to the same resource via a shared control block. When a shared ptr is copied, the count goes up. When a shared PDR is destroyed or reset, the count goes down. When the count reaches zero, the resource is automatically deleted.

Speaker 1

That sounds like it has more overhead than unique curtio.

Speaker 2

It does size of shared ptrt is larger than size of dqrt because it needs space for the pointer to the resource and a pointer to the control block. There's also the atomic overhead of incrementing and decrementing the reference count, which matters in multi threaded code, so you use it when you truly need shared ownership semantics.

Speaker 1

And for creating shared PTRs, the book strongly recommends sdd dot make shared. There's a performance reason for that, right and a huh yeah.

Speaker 2

Absolutely avoid doing shared ptrt. Always prefer std dot make shared T. The AHA moment is this, make shared performs a single memory allocation that holds both the object T and its control block together.

Speaker 1

Whereas new T followed by the shared ptr constructor does two separate allocations.

Speaker 2

It's exactly two allocations, means more overhead, potentially worse cash locality, The object in its counter might be far apart in memory, and critically a potential leak risk. Oh So imagine f shared ptrkeg. If G throws an exception after new T succeeds, but before the shared ptr constructor takes ownership, the newly allocated T object is leaked Forever. Make shared avoids this by doing the allocation and construction in a safer combined.

Speaker 1

Step clever, Okay, one more smart cooiner std dot weakptr. It's ones less useful, not.

Speaker 2

Less useful, just different. Std dot weekptr is a companion to std dot shared ptr. It holds a non owning reference to an object managed by shared ptr. It observes the object but doesn't affect its reference count. You can try to lock a weekptr to get a temporary shared ptr if the object still exists.

Speaker 1

What's the main use case?

Speaker 2

The absolute killer use case is breaking cycles. If you have two objects that hold shared PDRs to each other, they create a reference cycle. The reference counts will never drop to zero even when all external shared PDRs are gone memory leak. By having one object hold a weekpdr to the other, you break the cycle, allowing them to be destroyed correctly. It's essential for implementing caches or observer patterns safely with shared.

Speaker 1

Pdr right breaking those ownership loops. Okay, we've covered the standard tools. Let's go beyond the standard customization performance overloading memory allocation operators.

Speaker 2

Yes, C plus plus gives you the power to completely replace the default memory allocation functions. Operator new and operator delete. You can provide global overrides or member function overrides for specific classes.

Speaker 1

Why would you do that?

Speaker 2

Performance custom memory tracking using specialized allocators like pool allocators arenas. But as the book says, great power comes great responsibility. If you override these, you're the memory manager. Small mistates can have a huge impact, causing leaks, corruption, or performance degradation.

Speaker 1

Okay, so it's advanced stuff. What about placement new. The book calls it the most important version of operator new.

Speaker 2

Why because placement new doesn't actually allocate memory. Its signature looks like void operator newstd dot size void ptr. It takes a size and a pre allocated pointer ptr, and it simply returns that same pointer ptr.

Speaker 1

So what's the point.

Speaker 2

Its point is to construct an object at a specific memory location that you've already obtained. Somehow, it decouples allocation from construction.

Speaker 1

And where is that useful?

Speaker 2

Several places mapping C plus plus objects directly onto memory. MAPP hardware registers implementing custom containers that pre allocate large chunks of memory and then use placement new to construct objects within that chunk as needed, much faster than allocating each object individually. And it's key for optimizations like small

object optimization SOO or small string optimization SSO. In std dot function or std dot string, they might use internal buffer space and placement new for small object strings, avoiding heap allocation entirely.

Speaker 1

That makes sense, decoupling allocation and construction. The book even walks through writing a simple leak detector using operator overloading.

Speaker 2

Yes, a great practical example. Yeah, you overload global new and delete. In new, you allocate a bit extra memory, store the requested size and that extra bit at the beginning, and return a pointer just past that hidden size info.

Speaker 1

That's the lie it mentions, hiding the size exactly.

Speaker 2

Then in your overloaded delead you take the user's pointer back up slightly to read the hidden size, record that this block is now free, and then deallocate the entire blode, including the hidden size. You use some central accountant object to track what's allocated versus.

Speaker 1

Freed, and it uses the Meyer's singleton for that accountant. What's the catch there and the alignment issue?

Speaker 2

Right? The Meyer singleton uses a static local variable inside of function to ensure only one instance of the accountant is created, and it's done in a thread safe way. In modern c plus plus thanks to magic statics doing implicit syncretization. The catch is that this synchronization can add a small performance overhead. The critical pitfall, though, is alignment.

If you just store size it for the size, what if the object being allocated needs stricter alignment than size it, like an object needing sixteen by alignment, Your hidden size might throw off the alignment of the object itself.

Speaker 1

Out serious trouble. How do you fix that?

Speaker 2

The fix is clever. You allocate enough space at the start, not just for size it, but for std do maxiligned This type guarantees alignment suitable for any standard type. You store your size within that maxiline space and then return the pointer. After that guaranteed alignment boundary, you jump over the metadata space, ensuring the user's object is correctly aligned.

Speaker 1

Very neat, okay, moving faster now. Arena based memory management sounds fast.

Speaker 2

It could be blazingly fast. The idea is simple. Allocate one huge chunk of memory up front the arena. Then your custom operator new just bumps a pointer within that arena to hand out memory. A delete might do nothing at all. Nothing Often Yeah, you just use the arena for a specific phase like loading a game level, processing a request, and when you're done, you discard the entire arena in one go. Allocation becomes just pointer edition deallocation

becomes a no op until the end. Super fast, minimal fragmentation within the arena.

Speaker 1

Where is this used?

Speaker 2

Games are a classic example. Memory po level or seam high performance servers, memory per request compilers anywhere you have predictable phases of allocation and deallocation.

Speaker 1

And the performance numbers are impressive.

Speaker 2

The book site's examples showing custom new taking maybe seventy five percent of the time I'm a standard new, and custom delete being a tiny fraction like eight percent of the time of standard delete because it often does nothing. Huge gains possible if the pattern.

Speaker 1

Fits and chunked pools are related.

Speaker 2

Kind of an extension. Instead of one big arena for anything, you might have multiple pools, each managing fixed sized chunks of memory. Eg. A pool for thirty two byte objects, one for sixty four byte, et cetera. When you need an object of a certain size, you grab a pre allocated block from the corresponding pool. Very efficient for allocating many objects of a few common sizes.

Speaker 1

Okay, powerful stuff. Now let's look at the bleeding edge. C plus plus keeps evolving. C plus plus twenty three just landed. C plus plus twenty six, C plus plus twenty nine are coming. What's new in memory management?

Speaker 2

Right? The language doesn't stand still. These newer features often address long standing tricky problems or enable new optimizations. A big one in C plus plus twenty three is std dot start.

Speaker 1

Lifetimess what problem needed solving there.

Speaker 2

Imagine reading raw bytes from a network socket or a file into a SHAR buffer or std dot com byte buffer. You know those bytes represents a my packet strucked, but the C plus plus compiler doesn't. Officially no, mypacket object's lifetime has begun in that buffer.

Speaker 1

So just casting the buffer pointer to my packet is.

Speaker 2

Technically undefined behavior. It might seem to work by luck on your compiler, but it's not guaranteed or portable. STD dot start lifetime is my packet buffer ptr is the fix. It's like a magical no op function that doesn't generate code. What tells the compiler trust me, the lifetime of a mypacket object has now implicitly started at this location.

Speaker 1

AH, so it makes it common. Pattern is safe and portable that sounds like a huge relief for network programmers.

Speaker 2

Absolutely, the book calls it a potential boon to network programmers everywhere and anyone reading binary data. It closes a long standing ub loople.

Speaker 1

Okay, C plus plus twenty six mentions. Trivial relocation that sounds like an optimization.

Speaker 2

It's a potentially huge optimization. Normally, moving an object means copy constructing it at the destination and then destructing it at the source. Even with move semantics, there's still a copy of members and a destroy. Trivial relocation applies to types where moving them is equivalent to just doing a mebcapie of their bytes and leaving the source memory uninitialized. No constructor or destructor calls needed for the move itself.

Speaker 1

So for types like std dot vectorant, maybe moving it could just copy the pointer size and capacity bytes.

Speaker 2

Potentially Yes, If a type like maybe std dot uniquipdr or simple pod like structs or potentially containers under the right conditions is deemed trivially relocatable, the compiler can optimize moves down to a raw memory.

Speaker 1

Copy and the bed part.

Speaker 2

The best part, as the book highlights, is that existing code using std dot mob on these types compiled with previous standards could just run faster without changing a single line of source code when recompiled with a C plus plus twenty six compiler that implements this free performance.

Speaker 1

Nice okay, last one type of ware allocation and de allocation functions. What's this about?

Speaker 2

This is a really interesting propose. It allows overloading operator new and operator delete to take an extra argument STD dot type identity T, so.

Speaker 1

The allocator function itself knows the type of the object being allocated exactly.

Speaker 2

Currently, global operator new just gets a size request. It doesn't know if you're allocating an int, a STD dot string, or a huge custom class. With type awareness, the allocation function could potentially use different strategies based on the type T. Maybe allocate small objects from one pool, large objects from another, or objects of a specific class known to benefit from a certain memory layout from a dedicated arena.

Speaker 1

So it could integrate some of those custom arena pool benefits directly into the global allocation mechanism guided by type.

Speaker 2

That's the idea. It opens up new avenues for optimizations based on type information, potentially making customized allocation strategies easier to implement and integrate system wide. It's a really forward looking concept.

Speaker 1

Wow. Okay, we've really gone from the absolute fundamentals like an int being an object, all the way through the weeds of UGB, the elegance OFRIII and smart pointers, custom allocators, and now these cutting edge C plus plus twenty three twenty six features. It's amazing how much depth there is to C plus plus memory management and how Patresea's book lays it all out. It really gives you a sense of how the software connects to the actual machine.

Speaker 2

It really does. Yeah, and understating this stuff, even if you don't write C plus plus every day, it's incredibly valuable. These concepts ownership, resource, lifetime, careful memory handling. They echo in many other languages and systems. It empowers you to write code that's more robust, definitely more efficient, and ultimately safer.

Speaker 1

Absolutely, it makes you think differently about how programs work. So here's a final thought to leave you with. Considering this constant evolution we've just discussed, especially in C plus plus pilation, how might the whole concept of ownership in programming languages keep changing in the coming years. What new abstractions might we see built on top of these low level memory foundations we've explored today,

Transcript source: Provided by creator in RSS feed: download file
For the best experience, listen in Metacast app for iOS or Android