Skip to content

CT 301 — Ownership & Core Concepts

CT 301 · C++ Fundamentals

Ownership, Polymorphism,
and Smart Pointers

The ideas behind Homework 1
Why this matters

Memory is where C++ programs go wrong

  • Most serious bugs in large C++ codebases are memory bugs: use-after-free, leaks, double-free.
  • The fix is not "be more careful." It is making ownership explicit in the type system.
  • This homework teaches the modern answer: one owner per object, enforced by the compiler.

We use raw new/delete only in HW2, on purpose, so you feel why HW1's approach is safer.

Foundation

A class bundles data with behavior

  • An object holds data (members) and the functions that act on it.
  • Members are private; the outside world uses public methods.
  • A constructor sets up the object; a destructor cleans it up.
class Date {
public:
  Date(int y,int m,int d);   // constructor
  int year() const;          // behavior
private:
  int y_, m_, d_;             // data
};
Polymorphism

One interface, many behaviors

  • A base class declares virtual functions, a contract.
  • Derived classes override them. The same call does the right thing per type.
  • HW1: Item is the base; Equipment, Consumable, Borrowed behave differently.
bool Consumable::canLoan() const { return false; } // a cable: no
bool Equipment::canLoan()  const { return true;  } // a laptop: yes
// item->canLoan() picks the right one at run time

There is also a pure virtual (a function the base leaves unimplemented, = 0), which we covered in class. It forces every derived type to provide its own version. Same idea, stronger contract.

Polymorphism, two rules

Virtual destructor & no slicing

Virtual destructor

If you delete a derived object through a base pointer, the base needs a virtual ~Item(), or cleanup is wrong.

Don't slice

Store items as unique_ptr<Item>, never by value. Copying an Item by value chops off the derived part.

Both are provided/enforced in HW1, but you should know why they exist.

The big idea

Ownership: who is responsible for deleting it?

  • Every object's memory must be freed exactly once, by one owner.
  • The owner decides when the object dies. Everyone else is just looking.
  • In C++ the modern owner is std::unique_ptr<T>: one owner, frees automatically.

Deed vs. address. The owner holds the deed to the house. Everyone else just has the street address, they can visit and look, but they can't tear it down.

unique_ptr

One owner, freed automatically (RAII)

  • unique_ptr owns the object and deletes it for you when it goes out of scope.
  • You write no delete. This is RAII: the object's lifetime follows the owner's scope.
  • A unique_ptr cannot be copied, that would mean two owners. It can only be moved.
auto kbd = std::make_unique<Equipment>(...); // kbd owns it
// when kbd dies, the Equipment is deleted. No delete needed.
std::move

Transferring ownership = moving

  • To give ownership to someone else, you move the unique_ptr.
  • After the move, the old variable is empty; the new one owns the object.
  • The compiler forces this: you literally cannot copy it. That is the safety.
std::unique_ptr<Item> owned = extract(inStock_, id); // take it out
owned->setState(State::OnLoan);
onLoan_.push_back(std::move(owned));   // hand ownership over
// 'owned' is now empty; onLoan_ owns the item
Borrowing

A raw pointer here means "I'm just looking"

  • Our rule: a raw Item* is non-owning, it lets you read the item but never deletes it.
  • HW1 selections return std::vector<Item*>, borrowed pointers into items the inventory still owns.
  • Ownership is always a smart pointer (unique_ptr); raw pointers only borrow.

OK: raw pointer that borrows.   Banned in HW1: raw owning pointer, new, delete, malloc, calloc, realloc, free.

Borrow vs. take

Two operations, one careful distinction

gather = borrow

Copies non-owning Item* pointers so you can look at many items. Ownership stays put.

extract = take

Removes the item from its list and hands you the unique_ptr. Ownership moves to you.

Danger: after extract, that unique_ptr is the only thing keeping the item alive. Forget to move it into a list and the item is destroyed, not leaked.

Type vs. state

Two different "kinds" — keep them apart

Type (what it is)

Equipment / Consumable / Borrowed. Fixed for life. Polymorphism.

State (where it is)

InStock → OnLoan → back; or → Sold; or → Surplus. Changes over time. A small state machine.

The inventory keeps four lists, one per state. Changing state is moving the item's unique_ptr to the matching list.

A common question

Why not shared_ptr everywhere?

  • shared_ptr allows many owners and frees the object when the last one leaves.
  • Here that would be wrong by design: an item has exactly one home (the inventory) and one state.
  • It also costs more (reference counting) and hides the clear single-owner story we want to teach.

Use the simplest ownership that fits. For HW1 that is unique_ptr.

Hold onto these

Five things to remember

  • One owner per object. In HW1 the inventory owns every item.
  • unique_ptr frees automatically (RAII), you write no delete.
  • Transfer ownership by moving, never copying.
  • Raw pointers borrow, they look, they never delete.
  • Type is fixed; state changes. A state change is a move between lists.
Next

Now let's look at the homework

Part 2 puts these ideas to work: the inventory, the items, and what you'll build.