<aside> 🧠 Recall

IMG_3180.heic

Controller

<aside> đź’ˇ Exception Safety

void f() {
	C c;
	C *p = new C;
	g();
	delete p;
}

No leaks… but what if g throws ?

What if guranteed ?

void f() {
	C c;
	C *p new C;
	try {
		g();
	}
	catch(...) {
		delete p;
		throw;
	}
	delete p;
}

duplucation code

How else can we gurantee that something (eg. delete p;) will happen, no matter how we exit f ?

In some language “finally” clauses gurantee certain final actions - not in C++

Only thing you can count on in C++ - dtors for stack-allocated deta will run

<aside> đź’ˇ C++ Idiom: RAII - Resource Acquisition Is Initialization

Every resource should be wrapped in a stack-allocated object, whose job is to delete it

eg. Files

{       // ↓ acquiring the resource("file") = initializing the object(f)
	ifstream f {"file"}
	...
} // - the file is guranteed to be released when f is popped from stack
// (because f's dctor runs)

This can be done with dynamic memory

class std::unique_ptr(T) (import <memory>)
	// takes a T* in the ctor
	// the dtor will delete the ptr
	// in between - can determine, just like a ptr
void f() {
	C c;
	std::unique.ptr<C> p {new C};
	g();
} // - no leaks, guranteed

Alternatives

void f() {
	C c;
	//         allocates a C obj. on the heap, and puts a ptr
	//       ↓ to it inside a unique.ptr object
	auto p = std::make_unique<C>( );
	g();	  								//   ↑ ctor args go here, if there are any
}

</aside>

Difficulty:

unique_ptr<C> p {new C};
uniuqe_ptr<C> g = p;

What would happen if a unique.ptr were copied ?

template <typename T> class unique.ptr {
	T *ptr;
	public:
	explicit unique.ptr(T *p): ptr{p} {}
	~unique_ptr() {delete ptr;}
	unique_ptr(const unique.ptr &other) = delete;
	unique.ptr<T> &operator = (const unique_ptr &) = delete;
	unique.ptr(unique.ptr &&other): ptr{other.ptr} {
		other.ptr = nullptr;
	}
	unique.ptr<T> & operator(unique.ptr &&other) {
		if(this == &other) return *this;
		delete ptr;
		ptr = other.ptr;
		other.ptr = nullptr;
		return *this;
	}
	T &operator*() {return *ptr;}
	T *get() {return ptr;}
};

If you need to be able to copy ptrs - first answer the quesiton of

Ownership

<aside> đź’ˇ New understanding of ptrs:

<aside> đź«Ą Moving a unique.ptr = transfer of ownership

</aside>

ptrs as parameter

void f(unique_ptr<c>p);
	// f will **take ownership** of the object pointed to by p
	// - caller **loses custody** of the object
void g(C *p)
	// g will **NOT** tale over ownership of the object pointed to by p
	// - caller's ownership of the object **does not change**
	// (Note that the caller also might not own the obeject)

ptrs as result:

unique.ptr<c> f(); // return by value is always a move
// so f is **handing over ownership** of the C object to the caller
C *g(); // the ptr returned by g is understood not to be deleted
// by the caller. so it might represent a ptr to non-heap data,
// or to heap data that someone else already owns

</aside>

Rarely, a situatoion may arise thay calls for true shared ownership,

i.e. any of several ptrs might need to free the resource

{auto p = std::make_shared<c>();
	if(____) {
		auto g = p;
	} // - g popped, ptr **NOT** deleted
} // - p is popped, ptr **IS** deleted

shared_ptrs maintain a reference const - count of all share.ptr pointing as the same obj

Memory is freed when the # of shared.ptrs pointing to it wil reach 0

<aside> 🧠 Recall(Racket)

(define l1 (cons 1 (cons 2 (cons 3 empty))))

(define l2 (cons 4 (rest l1)))

IMG_3181.heic

</aside>