Featured image of post CS 246E

CS 246E

Object-Oriented Software Development (Enriched)

This course is Brad Lushman’s enriched version of CS 246, following his signature teaching style: problem-driven, densely packed with content, and incredibly rewarding. Rather than lecturing concepts in isolation, the course poses 34 problems and solves them to introduce C++ features organically. The final project is building a text editor with Vim-like functionality.

Course Overview

The central theme is mastering C++ through problem-solving — understanding not just what C++ features exist, but why they exist and when to use them. The course covers approximately 50% more content than regular CS 246, diving deep into move semantics, template metaprogramming, and design patterns.

Part I: Basic C++ (Problems 1-22)

Getting Started (Problems 1-4)

The course begins with fundamental I/O and program structure:

  • Problem 1: Hello World — iostream, std::cout, namespaces
  • Problem 2: Input/Output — cin, getline, I/O manipulators
  • Problem 3: Linear collections — vector, range-based for loops
  • Problem 4: Linear collections with iterators — iterator pattern, begin()/end()

Separate Compilation (Problems 5-7)

Building modular programs:

  • Problem 5: Separate compilation — header files, include guards, extern
  • Problem 6: Dependencies — Makefiles, automatic dependency tracking
  • Problem 7: Linear collections and memory — stack vs heap, new/delete, memory leaks

Classes and Resources (Problems 8-14)

The heart of C++ OOP:

  • Problem 8: Efficient iteration — custom iterators, operator overloading
  • Problem 9: Encapsulation — classes, access modifiers, friend
  • Problem 10: Abstraction and constructors — copy constructors, destructor basics
  • Problem 11: Copying — deep vs shallow copy, copy assignment operator
  • Problem 12: RAII — Resource Acquisition Is Initialization, exception safety
  • Problem 13: Move semantics — rvalue references, std::move, move constructor/assignment
  • Problem 14: The Rule of 5 — when to implement copy/move operations, = default, = delete

Advanced Class Features (Problems 15-22)

Templates, inheritance, and polymorphism:

  • Problem 15: Strong guarantees — exception safety levels (basic, strong, nothrow)
  • Problem 16: Abstraction with classes — abstract classes, pure virtual functions
  • Problem 17: Inheritance — IS-A relationship, protected members
  • Problem 18: Polymorphism — virtual methods, vtables, dynamic dispatch
  • Problem 19: Template functions — type deduction, template specialization
  • Problem 20: Smart pointers — unique_ptr, shared_ptr, weak_ptr
  • Problem 21: The visitor pattern — double dispatch, dynamic_cast
  • Problem 22: Composite and decorator patterns — structural design patterns

Part II: Object-Oriented Design

This section covers software design principles and patterns:

SOLID Principles

  1. Single Responsibility Principle (SRP): A class should have only one reason to change
  2. Open/Closed Principle (OCP): Open for extension, closed for modification
  3. Liskov Substitution Principle (LSP): Subtypes must be substitutable for their base types
  4. Interface Segregation Principle (ISP): Many specific interfaces are better than one general interface
  5. Dependency Inversion Principle (DIP): Depend on abstractions, not concretions

Design Patterns

The course covers several GoF patterns in depth:

  • Iterator: Traverse collections without exposing internal structure
  • Factory Method: Create objects without specifying exact class
  • Template Method: Define algorithm skeleton, defer steps to subclasses
  • Decorator: Add behavior dynamically without inheritance explosion
  • Visitor: Add operations to class hierarchies without modifying them
  • Observer: Publish-subscribe for loose coupling
  • Strategy: Encapsulate interchangeable algorithms

UML Diagrams

  • Class diagrams with associations, aggregation, composition
  • Sequence diagrams for object interactions
  • Dependency arrows and multiplicities

Part III: Abstraction in C++ (Problems 23-34)

Advanced C++ techniques for building robust abstractions:

Exception Safety and Resource Management (Problems 23-24)

  • Problem 23: Exception safety in STL — copy-and-swap idiom, strong guarantee
  • Problem 24: Abstraction over iterators — iterator categories, iterator_traits

Low-Level C++ Features (Problems 25-28)

  • Problem 25: POD types — Plain Old Data, standard layout, trivially copyable
  • Problem 26: Implementing std::move and std::forward — reference collapsing rules
  • Problem 27: Compile-time type traits — enable_if, SFINAE, type_traits header
  • Problem 28: CRTP — Curiously Recurring Template Pattern for static polymorphism

Metaprogramming and Advanced Patterns (Problems 29-34)

  • Problem 29: Resolving method calls at compile time — tag dispatch, constexpr if
  • Problem 30: Polymorphic cloning — virtual copy constructors via clone()
  • Problem 31: Logging with mixins — CRTP for cross-cutting concerns
  • Problem 32: Custom allocators — std::allocator, allocator-aware containers
  • Problem 33: Fixed-size allocators — memory pools, placement new
  • Problem 34: Empty Base Optimization (EBO) — zero-overhead abstractions

Key C++ Concepts

Memory Management

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// RAII pattern
class ResourceHolder {
    Resource* res;
public:
    ResourceHolder() : res(new Resource()) {}
    ~ResourceHolder() { delete res; }
    // Rule of 5: copy ctor, copy assign, move ctor, move assign, dtor
};

// Modern approach: smart pointers
auto ptr = std::make_unique<Resource>();
auto shared = std::make_shared<Resource>();

Move Semantics

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Rvalue reference and move
String(String&& other) noexcept
    : data(other.data), len(other.len) {
    other.data = nullptr;
    other.len = 0;
}

// Perfect forwarding
template<typename T>
void wrapper(T&& arg) {
    func(std::forward<T>(arg));
}

Template Metaprogramming

1
2
3
4
5
6
7
8
9
// SFINAE with enable_if
template<typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
process(T val);

// Compile-time conditionals (C++17)
if constexpr (std::is_pointer_v<T>) {
    // compile-time branch
}

Key Takeaways

After this course, you understand:

  1. Why C++ has move semantics — avoiding unnecessary copies, enabling efficient resource transfer
  2. How virtual functions work — vtables, dynamic dispatch overhead, when to use final
  3. Exception safety guarantees — designing robust APIs that don’t leak resources
  4. When to use templates vs inheritance — compile-time vs runtime polymorphism trade-offs
  5. SOLID principles in practice — building maintainable, extensible software
  6. Design patterns as vocabulary — communicating solutions to common problems
  7. Modern C++ idioms — RAII, smart pointers, move semantics, perfect forwarding

Recommendations

  • Prerequisites: CS 146 (or strong C background) is highly recommended
  • Start assignments early — they’re substantial and require deep understanding
  • Draw memory diagrams for move semantics and inheritance hierarchies
  • Implement the patterns yourself before using library versions
  • The Vim project is challenging but teaches real-world software design
  • Consider taking CS 146 first if you haven’t — similar teaching style, and it covers lower-level concepts that provide context for C++
Licensed under CC BY-NC-SA 4.0
Notes taking with heart
Built with Hugo
Theme Stack designed by Jimmy