C++ Class Design and OOP Fundamentals – CS 225, Weeks 1–2 Study Notes
offline

Source: CS 225 Data Structures, UIUC

Tags: Rule of Three, Rule of Five, Rule of Zero, copy constructor, destructor, copy assignment operator, const, templates, abstract data type, ADT, inheritance, polymorphism, operator overloading, C++ OOP

Difficulty: Introductory to Intermediate Prerequisites: Basic C++ syntax (variables, functions, classes). Familiarity with pointers helps but is covered in the companion notes on memory.


Big Picture

This material covers the building blocks of writing correct, well-structured C++ classes. If you have ever wondered why your program segfaults when you copy an object, or why the compiler generates five special member functions you never asked for, this is where those questions get answered. The concepts here (ownership semantics, templates, inheritance, polymorphism) underpin every data structure you will build for the rest of CS 225. You should be comfortable with basic class syntax before starting.


TL;DR

C++ classes that manage resources need explicit destructors, copy constructors, and copy assignment operators (the Rule of Three). Templates let you write data-structure code once and reuse it for any type. Inheritance lets you build new classes from existing ones, and polymorphism lets derived classes override behaviour so the same interface works across different types.


Key Terms

Rule of Three (Rule of Thirds)

If a class defines any one of a destructor, a copy constructor, or a copy assignment operator, it should define all three explicitly. The reason: if you need custom logic for one, the compiler-generated defaults for the others are almost certainly wrong.

In simple terms, think of it as: "If you are managing a resource manually, you need to handle every situation where that resource could be copied or destroyed."

Rule of Five

An extension of the Rule of Three for modern C++ (C++11 onwards). Adds the move constructor and the move assignment operator to the list, giving five special member functions total. Move semantics let you transfer ownership of resources cheaply instead of copying them.

In simple terms, this means: "Same idea as the Rule of Three, but now you also handle the case where an object is being moved rather than copied."

Rule of Zero

A corollary to the Rule of Five, attributed to Scott Meyers. Classes that manage ownership should declare custom destructors and copy/move operations. All other classes should declare none of them, relying on the compiler-generated defaults instead.

In simple terms: "If your class does not own a raw resource, let the compiler do the work."

Destructor

A special member function called automatically when an object goes out of scope or is deleted. Responsible for releasing any resources the object owns (heap memory, file handles, etc.).

Copy constructor

A constructor that creates a new object as a copy of an existing object. Signature: ClassName(const ClassName& other). The compiler generates a shallow copy by default, which is dangerous if the class owns heap memory.

Copy assignment operator

An overloaded operator= that assigns the contents of one existing object to another existing object. Must handle self-assignment and clean up the target's old resources before copying.

const

A keyword that tells the compiler a variable, parameter, or member function must not modify the thing it qualifies. Useful for signalling intent and catching bugs at compile time.

In simple terms: "Mark something const when it should not change, and the compiler will stop you if you accidentally try."

Template

A C++ feature that lets you write a class or function parameterised by type. Templates are recipes, not code: the compiler generates actual code when you instantiate the template with a concrete type. Template definitions go in the header file because the compiler needs the full definition at the point of instantiation.

In simple terms: "Write it once with a placeholder type, and the compiler stamps out a version for int, string, or whatever you need."

Abstract data type (ADT)

A mathematical model for a data structure that specifies the operations and their behaviour, but not the implementation. Examples: list, queue, map, stack. The ADT says what you can do; the implementation says how.

In simple terms: "An ADT is the contract. The actual code behind it is the implementation."

Inheritance

A mechanism where a new class (derived class) is built from an existing class (base class), automatically gaining its member functions and variables. The derived class can then add new members or override existing ones.

Polymorphism

The ability for a function call to behave differently depending on the type of object it operates on. In C++, this is achieved through inheritance and virtual functions: a derived class redefines a function from the base class, and the correct version is called at runtime via a base-class pointer or reference.

In simple terms: "Same function name, different behaviour depending on the actual object type."

Operator overloading

C++ allows you to redefine the meaning of most operators (+, -, ==, <<, [], etc.) for your own classes. This lets user-defined types behave like built-in types. Categories of overloadable operators include arithmetic, bitwise, assignment, comparison, logical, and special operators like [], (), and ->.


Core Content

The Rule of Three in Practice

  • If your class allocates memory with new, you need all three:

    • A destructor to delete it

    • A copy constructor to deep-copy it into a new object

    • A copy assignment operator to deep-copy it into an existing object

  • The compiler-generated defaults perform shallow (member-wise) copies, which leads to double-free bugs when two objects point to the same heap memory

  • The Rule of Five extends this for move semantics (C++11): add a move constructor and a move assignment operator for efficient transfers of ownership

Templates and Generic Programming

  • Templates prioritise code over types: you write the logic once, and the compiler generates type-specific versions

  • Templates are instantiated at compile time, so the full definition must be visible to the compiler (put them in the header file, not the .cpp file)

  • Because templates are expanded at compile time, errors in template code surface during compilation, not at runtime

Abstract Data Types

  • An ADT defines behaviour (operations and their contracts) without specifying implementation

  • The same ADT can have multiple implementations: a List ADT, for example, can be backed by an array or by linked memory

  • The contents and type of data stored do not matter to the ADT itself, which is why templates pair naturally with ADT implementations

Inheritance and Polymorphism

  • Inheritance applies to classes: the derived class inherits all public and protected members from the base class

  • Polymorphism applies to functions and methods: a derived class provides its own implementation of a function declared (typically virtual) in the base class

  • Polymorphism requires inheritance; you cannot have polymorphic behaviour without a class hierarchy

  • At runtime, a call through a base-class pointer or reference dispatches to the correct derived-class function (this is dynamic dispatch via the vtable)

Compile-Time vs. Runtime Errors

  • Compile-time errors are caught when code is translated to machine language: syntax errors, type mismatches, missing declarations

  • Runtime errors occur during execution: null-pointer dereferences, out-of-bounds access, operations on invalid data


Real-World Applications

The Rule of Three matters whenever you write a class that wraps a system resource, such as a file handle, a network socket, or a GPU buffer. Game engines, database drivers, and operating-system kernels all rely on getting resource ownership right. Templates are the backbone of the C++ Standard Library: std::vector, std::map, and std::sort are all templates.


Common Misconceptions

  • Students often think the compiler-generated copy constructor does a deep copy. It does not; it copies each member by value, which for pointers means copying the address, not the data it points to.

  • Students sometimes believe templates are executed at runtime. Templates are expanded entirely at compile time; by the time the program runs, the template is gone and only concrete, type-specific code remains.

  • Polymorphism and inheritance are sometimes confused as the same thing. Inheritance is about structure (what a class has). Polymorphism is about behaviour (how a function call resolves). You need the first to get the second, but they are distinct concepts.

  • The Rule of Zero does not mean "never write a destructor." It means: isolate resource management into dedicated classes (like smart pointers), and let everything else rely on defaults.


Why It Matters / Exam Flags

⚠️ Be able to identify when the Rule of Three is violated and describe what goes wrong (double-free, memory leak).

⚠️ Know why template code must live in the header file.

⚠️ Distinguish inheritance (classes) from polymorphism (functions/methods).

⚠️ Know the difference between compile-time and runtime errors, and give examples of each.


Quick Self-Test

  1. True or False: If a class defines a custom destructor, the compiler-generated copy constructor is safe to use.

  1. Fill in the blank: Templates are instantiated at ______ time.

  1. True or False: Polymorphism can be achieved without inheritance in C++.

  1. Fill in the blank: An abstract data type specifies ______ but not ______.

  1. True or False: A const member function is allowed to modify the object's data members.


Practice Q&A

Q: What is the Rule of Three, and why does it exist?

A: If a class defines a destructor, copy constructor, or copy assignment operator, it should define all three. It exists because needing custom logic for any one of them implies the compiler defaults for the others will handle resources incorrectly (typically leading to shallow copies and double-free bugs).

Q: Why must C++ template definitions be placed in the header file?

A: Templates are expanded at compile time. The compiler needs the full template definition at every point where the template is instantiated. If the definition is in a .cpp file, other translation units cannot see it and compilation fails.

Q: Explain the difference between inheritance and polymorphism.

A: Inheritance is a structural relationship where a derived class gains the members of a base class. Polymorphism is a behavioural property where a function call resolves to different implementations depending on the runtime type of the object. Polymorphism depends on inheritance but adds the ability to override and dynamically dispatch function calls.

Q: Give one example each of a compile-time error and a runtime error.

A: Compile-time: using an undeclared variable (syntax/semantic error caught during compilation). Runtime: dereferencing a null pointer (the code compiles fine, but crashes during execution).

Q: What does the const keyword do when applied to a member function?

A: It promises that the member function will not modify any of the object's data members. The compiler enforces this, so attempting to modify a member inside a const function produces a compile-time error.


Connections to Other Topics

  • The Rule of Three connects directly to memory management (pointers, new, delete), covered in the companion notes on pointers and references.

  • Abstract data types are the conceptual framework for every data structure in CS 225: lists, stacks, queues, trees, and graphs are all ADTs with multiple possible implementations.

  • Templates connect to the STL (Standard Template Library), which you will use extensively for containers and algorithms throughout the course.


Related Terms / Search Tags

Rule of Three, Rule of Five, Rule of Zero, Big Three C++, copy constructor, copy assignment operator, destructor, move semantics, const keyword, C++ templates, generic programming, header-only, abstract data type, ADT, inheritance, polymorphism, virtual function, dynamic dispatch, vtable, operator overloading, compile-time error, runtime error, CS 225 UIUC, data structures C++