From 3b7a23a323b3bf993424747ab3e82d799055db3a Mon Sep 17 00:00:00 2001 From: Trance-0 <60459821+Trance-0@users.noreply.github.com> Date: Wed, 5 Mar 2025 00:49:12 -0600 Subject: [PATCH] update --- pages/CSE332S/CSE332S_L13.md | 429 +++++++++++++++---------- pages/CSE332S/CSE332S_L14.md | 224 +++++++++++++ public/CSE332S/CPP_Function_Memory.png | Bin 0 -> 17405 bytes 3 files changed, 481 insertions(+), 172 deletions(-) create mode 100644 pages/CSE332S/CSE332S_L14.md create mode 100644 public/CSE332S/CPP_Function_Memory.png diff --git a/pages/CSE332S/CSE332S_L13.md b/pages/CSE332S/CSE332S_L13.md index 47c6475..07f73de 100644 --- a/pages/CSE332S/CSE332S_L13.md +++ b/pages/CSE332S/CSE332S_L13.md @@ -1,224 +1,309 @@ # CSE332S Lecture 13 -## Copy control +## Memory layout of a C++ program, variables and their lifetimes -Copy control consists of 5 distinct operations +### C++ Memory Overview -- A `copy constructor` initializes an object by duplicating the const l-value that was passed to it by reference -- A `copy-assignment operator` (re)sets an object's value by duplicating the const l-value passed to it by reference -- A `destructor` manages the destruction of an object -- A `move constructor` initializes an object by transferring the implementation from the r-value reference passed to it (next lecture) -- A `move-assignment operator` (re)sets an object's value by transferring the implementation from the r-value reference passed to it (next lecture) +4 major memory segments -Today we'll focus on the first 3 operations and will defer the others (introduced in C++11) until next time +- Global: variables outside stack, heap +- Code (a.k.a. text): the compiled program +- Heap: dynamically allocated variables +- Stack: parameters, automatic and temporary variables (all the variables that are declared inside a function, managed by the compiler, so must be fixed size) +- _For the dynamically allocated variables, they will be allocated in the heap segment, but the pointer (fixed size) to them will be stored in the stack segment._ -- The others depend on the new C++11 `move semantics` +Key differences from Java -### Basic copy control operations +- Destructors of automatic variables called when stack frame where declared pops +- No garbage collection: program must explicitly free dynamic memory -A copy constructor or copy-assignment operator takes a reference to a (usually const) instance of the class +Heap and stack use varies dynamically -- Copy constructor initializes a new object from it -- Copy-assignment operator sets object's value from it -- In either case, original the object is left unchanged (which differs from the move versions of these operations) -- Destructor takes no arguments `~A()` (except implicit `this`) +Code and global use is fixed -Copy control operations for built-in types - -- Copy construction and copy-assignment copy values -- Destructor of built-in types does nothing (is a "no-op") - -Compiler-synthesized copy control operations - -- Just call that same operation on each member of the object -- Uses defined/synthesized definition of that operation for user-defined types (see above for built-in types) - -### Preventing or Allowing Basic Copy Control - -Old (C++03) way to prevent compiler from generating a default constructor, copy constructor, destructor, or assignment operator was somewhat awkward - -- Declare private, don't define, don't use within class -- This works, but gives cryptic linker error if operation is used - -New (C++11) way to prevent calls to any method - -- End the declaration with `= delete` (and don't define) -- Compiler will then give an intelligible error if a call is made - -C++11 allows a constructor to call peer constructors - -- Allows re-use of implementation (through delegation) -- Object is fully constructed once any constructor finishes - -C++11 lets you ask compiler to synthesize operations - -- Explicitly, but only for basic copy control, default constructor -- End the declaration with `= default` (and don't define) The compiler will then generate the operation or throw an error if it can't. - -## Shallow vs Deep Copy - -### Shallow Copy Construction +Code segment is "read-only" ```cpp -// just uses the array that's already in the other object -IntArray::IntArray(const IntArray &a) - :size_(a.size_), - values_(a.values_) { - // only memory address is copied, not the memory it points to +int g_default_value = 1; + +int main (int argc, char **argv) { + Foo *f = new Foo; + + f->setValue(g_default_value); + + delete f; // programmer must explicitly free dynamic memory + + return 0; } -int main(int argc, char * argv[]){ - IntArray arr = {0,1,2}; - IntArray arr2 = arr; - return 0; +void Foo::setValue(int v) { + this->m_value = v; } ``` -There are two ways to "copy" +![Image of memory layout](https://notenextra.trance-0.com/images/CSE332S/CPP_Function_Memory.png) -- Shallow: re-aliases existing resources - - E.g., by copying the address value from a pointer member variable -- Deep: makes a complete and separate copy - - I.e., by following pointers and deep copying what they alias +### Memory, Lifetimes, and Scopes -Version above shows shallow copy +Temporary variables -- Efficient but may be risky (why?) The destructor will delete the memory that the other object is pointing to. -- Usually want no-op destructor, aliasing via `shared_ptr` or a boolean value to check if the object is the original memory allocator for the resource. +- Are scoped to an expression, e.g., `a = b + 3 * c;` -### Deep Copy Construction +Automatic (stack) variables + +- Are scoped to the duration of the function in which they are declared + +Dynamically allocated variables + +- Are scoped from explicit creation (new) to explicit destruction (delete) + +Global variables + +- Are scoped to the entire lifetime of the program +- Includes static class and namespace members +- May still have initialization ordering issues + +Member variables + +- Are scoped to the lifetime of the object within which they reside +- Depends on whether object is temporary, automatic, dynamic, or global + +**Lifetime of a pointer/reference can differ from the lifetime of the location to which it points/refers** + +## Direct Dynamic Memory Allocation and Deallocation ```cpp -IntArray::IntArray(const IntArray &a) - :size_(0), values_(nullptr) { - - if (a.size_ > 0) { - // new may throw bad_alloc, - // set size_ after it succeeds - values_ = new int[a.size_]; - size_ = a.size_; - - // could use memcpy instead - for (size_t i = 0; - i < size_; ++i) { - values_[i] = a.values_[i]; - } +#include +using namespace std; +int main (int, char *[]) { + int * i = new int; // any of these can throw bad_alloc + int * j = new int(3); + int * k = new int[*j]; + int * l = new int[*j]; + for (int m = 0; m < *j; ++m) { // fill the array with loop + l[m] = m; } + delete i; // call int destructor + delete j; // single destructor call + delete [] k; // call int destructor for each element + delete [] l; + return 0; } -int main(int argc, char * argv[]){ - IntArray arr = {0,1,2}; - IntArray arr2 = arr; - return 0; -} - ``` -This code shows deep copy +## Issues with direct memory management -- Safe: no shared aliasing, exception aware initialization -- But may not be as efficient as shallow copy in many cases - -Note trade-offs with arrays - -- Allocate memory once -- More efficient than multiple calls to new (heap search) -- Constructor and assignment called on each array element -- Less efficient than block copy - - E.g., using `memcpy()` -- But sometimes necessary - - i.e., constructors, destructors establish needed invariants - -Each object is responsible for its own resources. - -## Swap Trick for Copy-Assignment - -The swap trick is a way to implement the copy-assignment operator, given that the `size_` and `values_` members are already defined in constructor. +### A Basic Issue: Multiple Aliasing ```cpp -class Array { -public: - Array(unsigned int) ; // assume constructor allocates memory - Array(const Array &); // assume copy constructor makes a deep copy - ~Array(); // assume destructor calls delete on values_ - Array & operator=(const Array &a); -private: - size_t size_; - int * values_; -}; - -Array & Array::operator=(const Array &a) { // return ref lets us chain - if (&a != this) { // note test for self-assignment (safe, efficient) - Array temp(a); // copy constructor makes deep copy of a - swap(temp.size_, size_); // note unqualified calls to swap - swap(temp.values_, values_); // (do user-defined or std::swap) - } - return *this; // previous *values_ cleaned up by temp's destructor, which is the member variable of the current object +int main (int argc, char **argv) { + Foo f; + Foo *p = &f; + Foo &r = f; + delete p; + return 0; } - -int main(int argc, char * argv[]){ - IntArray arr = {0,1,2}; - IntArray arr2 = {3,4,5}; - arr2 = arr; - return 0; -} - ``` -## Review: Construction/destruction order with inheritance, copy control with inheritance +Multiple aliases for same object -### Constructor and Destructor are Inverses +- `f` is a simple alias, the object itself +- `p` is a variable holding a pointer +- `r` is a variable holding a reference + +What happens when we call delete on p? + +- Destroy a stack variable (may get a bus error there if we’re lucky) +- If not, we may crash in destructor of f at function exit +- Or worse, a local stack corruption that may lead to problems later + +Problem: object destroyed but another alias to it was then used (**dangling pointer issue**) + +### Memory Lifetime Errors ```cpp -IntArray::IntArray(unsigned int u) - : size_(0), values_(nullptr) { - // exception safe semantics - values_ = new int [u]; - size_ = u; +Foo *bad() { + Foo f; + return &f; // return address of local variable, f is destroyed after function returns } -IntArray::~IntArray() { +Foo &alsoBad() { + Foo f; + return f; // return reference to local variable, f is destroyed after function returns +} - // deallocates heap memory - // that values_ points to, - // so it's not leaked: - // with deep copy, object - // owns the memory - delete [] values_; +Foo mediocre() { + Foo f; + return f; // return copy of local variable, f is destroyed after function returns, danger when f is a large object +} - // the size_ and values_ - // member variables are - // themselves destroyed - // after destructor body +Foo * good() { + Foo *f = new Foo; + return f; // return pointer to local variable, with new we can return a pointer to a dynamically allocated object, but we must remember to delete it later +} + +int main() { + Foo *f = &mediocre(); // f is a pointer to a temporary object, which is destroyed after function returns, f is invalid after function returns + cout << good()->value() << endl; // good() returns a pointer to a dynamically allocated object, but we did not store the pointer, so it will be lost after function returns, making it impossible to delete it later. + return 0; } ``` -Constructors initialize -- At the start of each object's lifetime -- Implicitly called when object is created +Automatic variables -Destructors clean up +- Are destroyed on function return +- But in bad, we return a pointer to a variable that no longer exists +- Reference from also_bad similar +- Like an un-initialized pointer -- Implicitly called when an object is destroyed - - E.g., when stack frame where it was declared goes out of scope - - E.g., when its address is passed to delete - - E.g., when another object of which it is a member is being destroyed +What if we returned a copy? -### More on Initialization and Destruction +- Ok, we avoid the bad pointer, and end up with an actual object +- But we do twice the work (why?) +- And, it’s a temporary variable (more on this next) -Initialization follows a well defined order +We really want dynamic allocation here -- Base class constructor is called - - That constructor recursively follows this order, too -- Member constructors are called - - In order members were declared - - Good style to list in that order (a good compiler may warn if not) -- Constructor body is run +Dynamically allocated variables -Destruction occurs in the reverse order +- Are not garbage collected +- But are lost if no one refers to them: called a "**memory leak**" -- Destructor body is run, then member destructors, then base class destructor (which recursively follows reverse order) +Temporary variables -**Make destructor virtual if members are virtual** +- Are destroyed at end of statement +- Similar to problems w/ automatics -- Or if class is part of an inheritance hierarchy -- Avoids “slicing”: ensures destruction starts at the most derived class destructor (not at some higher base class) +Can you spot 2 problems? + +- One with a temporary variable +- One with dynamic allocation + +### Double Deletion Errors + +```cpp +int main (int argc, char **argv) { + Foo *f = new Foo; + delete f; + // ... do other stuff + delete f; // will throw an error because f is already deleted + return 0; +} +``` + +What could be at this location? + +- Another heap variable +- Could corrupt heap + +## Shared pointers and the RAII idiom + +### A safer approach using smart pointers + +C++11 provides two key dynamic allocation features + +- `shared_ptr` : a reference counted pointer template to alias and manage objects allocated in dynamic memory (we’ll mostly use the shared_ptr smart pointer in this course) +- `make_shared` : a function template that dynamically allocates and value initializes an object and then returns a shared pointer to it (hiding the object’s address, for safety) + +C++11 provides 2 other smart pointers as well + +- `unique_ptr` : a more complex but potentially very efficient way to transfer ownership of dynamic memory safely (implements C++11 “move semantics”) +- `weak_ptr` : gives access to a resource that is guarded by a shared_ptr without increasing reference count (can be used to prevent memory leaks due to circular references) + +### Resource Acquisition Is Initialization (RAII) + +Also referred to as the "Guard Idiom" + +- However, the term "RAII" is more widely used for C++ + +Relies on the fact that in C++ a stack object’s destructor is called when stack frame pops + +Idea: we can use a stack object (usually a smart pointer) to hold the ownership of a heap object, or any other resource that requires explicit clean up + +- Immediately initialize stack object with the allocated resource +- De-allocate resource in the stack object’s destructor + +### Example: Resource Acquisition Is Initialization (RAII) + +```cpp +shared_ptr createAndInit() { + shared_ptr p = + make_shared (); + init(p);// may throw exception + return p; +} + +int run () { + try { + shared_ptr spf = + createAndInit(); + cout << “*spf is ” << *spf; + } catch (...) { + return -1 + } + return 0; +} +``` + +RAII idiom example using shared_ptr + +```cpp +#include +using namespace std; +``` + +- `shared_ptr` assumes and maintains ownership of aliased X +- Can access the aliased X through it (*spf) +- `shared_ptr` destructor calls delete on address of owned X when it’s safe to do so (per reference counting idiom discussed next) +- Combines well with other memory idioms + +### Reference Counting + +Basic Problem + +- Resource sharing is often more efficient than copying +- But it’s hard to tell when all are done using a resource +- Must avoid early deletion +- Must avoid leaks (non-deletion) + +Solution Approach + +- Share both the resource and a counter for references to it +- Each new reference increments the counter +- When a reference is done, it decrements the counter +- If count drops to zero, also deletes resource and counter +- "last one out shuts off the lights" + +### Reference Counting Example + +```cpp +shared_ptr createAndInit() { + shared_ptr p = + make_shared (); + init(p);// may throw exception + return p; +} + +int run () { + try { + shared_ptr spf = + createAndInit(); + shared_ptr spf2 = spf; + // object destroyed after + // both spf and spf2 go away + } catch (...) { + return -1 + } + return 0; +} +``` + +Again starts with RAII idiom via shared_ptr + +- `spf` initially has sole ownership of aliased X +- `spf.unique()` would return true +- `spf.use_count` would return 1 + +`shared_ptr` copy constructor increases count, and its destructor decreases count + +`shared_ptr` destructor calls delete on the pointer to the owned X when count drops to 0 diff --git a/pages/CSE332S/CSE332S_L14.md b/pages/CSE332S/CSE332S_L14.md new file mode 100644 index 0000000..16db469 --- /dev/null +++ b/pages/CSE332S/CSE332S_L14.md @@ -0,0 +1,224 @@ +# CSE332S Lecture 14 + +## Copy control + +Copy control consists of 5 distinct operations + +- A `copy constructor` initializes an object by duplicating the const l-value that was passed to it by reference +- A `copy-assignment operator` (re)sets an object's value by duplicating the const l-value passed to it by reference +- A `destructor` manages the destruction of an object +- A `move constructor` initializes an object by transferring the implementation from the r-value reference passed to it (next lecture) +- A `move-assignment operator` (re)sets an object's value by transferring the implementation from the r-value reference passed to it (next lecture) + +Today we'll focus on the first 3 operations and will defer the others (introduced in C++11) until next time + +- The others depend on the new C++11 `move semantics` + +### Basic copy control operations + +A copy constructor or copy-assignment operator takes a reference to a (usually const) instance of the class + +- Copy constructor initializes a new object from it +- Copy-assignment operator sets object's value from it +- In either case, original the object is left unchanged (which differs from the move versions of these operations) +- Destructor takes no arguments `~A()` (except implicit `this`) + +Copy control operations for built-in types + +- Copy construction and copy-assignment copy values +- Destructor of built-in types does nothing (is a "no-op") + +Compiler-synthesized copy control operations + +- Just call that same operation on each member of the object +- Uses defined/synthesized definition of that operation for user-defined types (see above for built-in types) + +### Preventing or Allowing Basic Copy Control + +Old (C++03) way to prevent compiler from generating a default constructor, copy constructor, destructor, or assignment operator was somewhat awkward + +- Declare private, don't define, don't use within class +- This works, but gives cryptic linker error if operation is used + +New (C++11) way to prevent calls to any method + +- End the declaration with `= delete` (and don't define) +- Compiler will then give an intelligible error if a call is made + +C++11 allows a constructor to call peer constructors + +- Allows re-use of implementation (through delegation) +- Object is fully constructed once any constructor finishes + +C++11 lets you ask compiler to synthesize operations + +- Explicitly, but only for basic copy control, default constructor +- End the declaration with `= default` (and don't define) The compiler will then generate the operation or throw an error if it can't. + +## Shallow vs Deep Copy + +### Shallow Copy Construction + +```cpp +// just uses the array that's already in the other object +IntArray::IntArray(const IntArray &a) + :size_(a.size_), + values_(a.values_) { + // only memory address is copied, not the memory it points to +} + +int main(int argc, char * argv[]){ + IntArray arr = {0,1,2}; + IntArray arr2 = arr; + return 0; +} +``` + +There are two ways to "copy" + +- Shallow: re-aliases existing resources + - E.g., by copying the address value from a pointer member variable +- Deep: makes a complete and separate copy + - I.e., by following pointers and deep copying what they alias + +Version above shows shallow copy + +- Efficient but may be risky (why?) The destructor will delete the memory that the other object is pointing to. +- Usually want no-op destructor, aliasing via `shared_ptr` or a boolean value to check if the object is the original memory allocator for the resource. + +### Deep Copy Construction + +```cpp +IntArray::IntArray(const IntArray &a) + :size_(0), values_(nullptr) { + + if (a.size_ > 0) { + // new may throw bad_alloc, + // set size_ after it succeeds + values_ = new int[a.size_]; + size_ = a.size_; + + // could use memcpy instead + for (size_t i = 0; + i < size_; ++i) { + values_[i] = a.values_[i]; + } + } +} +int main(int argc, char * argv[]){ + IntArray arr = {0,1,2}; + IntArray arr2 = arr; + return 0; +} + +``` + +This code shows deep copy + +- Safe: no shared aliasing, exception aware initialization +- But may not be as efficient as shallow copy in many cases + +Note trade-offs with arrays + +- Allocate memory once +- More efficient than multiple calls to new (heap search) +- Constructor and assignment called on each array element +- Less efficient than block copy + - E.g., using `memcpy()` +- But sometimes necessary + - i.e., constructors, destructors establish needed invariants + +Each object is responsible for its own resources. + +## Swap Trick for Copy-Assignment + +The swap trick is a way to implement the copy-assignment operator, given that the `size_` and `values_` members are already defined in constructor. + +```cpp +class Array { +public: + Array(unsigned int) ; // assume constructor allocates memory + Array(const Array &); // assume copy constructor makes a deep copy + ~Array(); // assume destructor calls delete on values_ + Array & operator=(const Array &a); +private: + size_t size_; + int * values_; +}; + +Array & Array::operator=(const Array &a) { // return ref lets us chain + if (&a != this) { // note test for self-assignment (safe, efficient) + Array temp(a); // copy constructor makes deep copy of a + swap(temp.size_, size_); // note unqualified calls to swap + swap(temp.values_, values_); // (do user-defined or std::swap) + } + return *this; // previous *values_ cleaned up by temp's destructor, which is the member variable of the current object +} + +int main(int argc, char * argv[]){ + IntArray arr = {0,1,2}; + IntArray arr2 = {3,4,5}; + arr2 = arr; + return 0; +} + +``` + +## Review: Construction/destruction order with inheritance, copy control with inheritance + +### Constructor and Destructor are Inverses + +```cpp +IntArray::IntArray(unsigned int u) + : size_(0), values_(nullptr) { + // exception safe semantics + values_ = new int [u]; + size_ = u; +} + +IntArray::~IntArray() { + + // deallocates heap memory + // that values_ points to, + // so it's not leaked: + // with deep copy, object + // owns the memory + delete [] values_; + + // the size_ and values_ + // member variables are + // themselves destroyed + // after destructor body +} +``` +Constructors initialize + +- At the start of each object's lifetime +- Implicitly called when object is created + +Destructors clean up + +- Implicitly called when an object is destroyed + - E.g., when stack frame where it was declared goes out of scope + - E.g., when its address is passed to delete + - E.g., when another object of which it is a member is being destroyed + +### More on Initialization and Destruction + +Initialization follows a well defined order + +- Base class constructor is called + - That constructor recursively follows this order, too +- Member constructors are called + - In order members were declared + - Good style to list in that order (a good compiler may warn if not) +- Constructor body is run + +Destruction occurs in the reverse order + +- Destructor body is run, then member destructors, then base class destructor (which recursively follows reverse order) + +**Make destructor virtual if members are virtual** + +- Or if class is part of an inheritance hierarchy +- Avoids “slicing”: ensures destruction starts at the most derived class destructor (not at some higher base class) diff --git a/public/CSE332S/CPP_Function_Memory.png b/public/CSE332S/CPP_Function_Memory.png new file mode 100644 index 0000000000000000000000000000000000000000..79e67e10064aad9bccffcb934eb457054c7bb75e GIT binary patch literal 17405 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 literal 0 HcmV?d00001