Skip to content

Teaching Notes: Module 01

  • Stack vs Heap allocation
  • new and delete operators
  • References vs Pointers
  • Pointers to member functions
  • switch statement

  • Class design with constructor/destructor
  • new for heap allocation
  • Understanding when to use stack vs heap
  1. Not understanding when to use heap

    • newZombie() returns a pointer - must use heap (survives function)
    • randomChump() uses stack (destroyed at end of function)
  2. Memory leaks

    Zombie* z = newZombie("Bob");
    // Student forgets: delete z;
  3. Destructor message missing

    • Subject requires destructor to print zombie name
  • “If you create a zombie inside a function and return it, will it still exist outside?”
  • “What happens to stack variables when a function ends?”
  • “How do you free memory allocated with new?”

  • Array allocation with new[]
  • Proper deallocation with delete[]
  • Placement new (optional advanced concept)
  1. Using delete instead of delete[]

    Zombie* horde = new Zombie[N];
    delete horde; // WRONG!
    delete[] horde; // CORRECT
  2. Not initializing zombie names

    • Must set name for each zombie in the array
  3. Returning local array

    Zombie* zombieHorde(int N, std::string name) {
    Zombie horde[N]; // WRONG - stack array, dies at return
    return horde;
    }
  • “What’s the difference between delete and delete[]?”
  • “How do you set the name of each zombie in an array?”

  • Understanding that references are aliases
  • Address comparison between variable, pointer, and reference

All three should print the same address and value:

std::string str = "HI THIS IS BRAIN";
std::string* stringPTR = &str;
std::string& stringREF = str;
&str == stringPTR == &stringREF // All same address
str == *stringPTR == stringREF // All same value
  • Confusing & (address-of) with & (reference declaration)
  • Not understanding that stringREF IS str, not a copy

  • When to use reference vs pointer as class member
  • Understanding reference initialization requirements
HumanAHumanB
Always has weaponMay not have weapon
Weapon in constructorWeapon set later
Use referenceUse pointer
  1. Using pointer for HumanA

    • Works but misses the point of the exercise
  2. Reference not initialized in constructor

    class HumanA {
    Weapon& _weapon; // Must be initialized!
    public:
    HumanA(std::string name) {} // ERROR: _weapon not initialized
    };
  3. Not checking NULL for HumanB

    void HumanB::attack() {
    // Must check if _weapon is NULL before using
    }
  • “Can a reference be NULL?”
  • “Can a reference be changed to refer to something else after initialization?”
  • “If HumanB might not have a weapon, what type should we use?”

  • File I/O with ifstream/ofstream
  • String manipulation without std::string::replace
  • Creating output file with .replace extension
  1. Using forbidden std::string::replace

    • Must use find + substr + concatenation
  2. Not handling file errors

    std::ifstream file(filename);
    if (!file.is_open()) {
    // Must handle this!
    }
  3. Not handling multiple occurrences

    • Must replace ALL occurrences, not just first
std::string replaceAll(std::string str, const std::string& from, const std::string& to) {
size_t pos = 0;
while ((pos = str.find(from, pos)) != std::string::npos) {
str = str.substr(0, pos) + to + str.substr(pos + from.length());
pos += to.length();
}
return str;
}

  • Pointers to member functions
  • Avoiding if/else chains
void (Harl::*funcs[4])() = {
&Harl::debug,
&Harl::info,
&Harl::warning,
&Harl::error
};
std::string levels[4] = {"DEBUG", "INFO", "WARNING", "ERROR"};
for (int i = 0; i < 4; i++) {
if (level == levels[i]) {
(this->*funcs[i])();
return;
}
}
  1. Using if/else forest

    • Subject explicitly forbids this
  2. Wrong syntax for calling member function pointer

    funcs[i](); // WRONG
    (this->*funcs[i])(); // CORRECT

  • switch statement
  • Fall-through behavior
switch (level) {
case DEBUG:
debug();
// NO BREAK - fall through!
case INFO:
info();
case WARNING:
warning();
case ERROR:
error();
break;
default:
std::cout << "[ Probably complaining about insignificant problems ]" << std::endl;
}
  1. Adding break to each case

    • Subject wants fall-through for filtering
  2. Using strings in switch

    • C++98 doesn’t support switch on strings
    • Must convert to int/enum first
  • “What happens if you don’t put break in a switch case?”
  • “Can you switch on a string in C++98?”

Draw diagrams showing:

  • Stack frame with local variables
  • Heap with allocated memory
  • Pointers as arrows to memory locations
  • References as alternative names for same memory
  1. “Why can’t references be NULL?”

    • Reference is an alias, must refer to something
    • Pointer is an address, can be “no address” (NULL)
  2. “Why use references at all?”

    • Cleaner syntax (no * to dereference)
    • Cannot be null (one less error case)
    • Cannot be reassigned (makes intent clear)
  3. “When stack, when heap?”

    • Stack: Known size, dies with scope, fast
    • Heap: Dynamic size, manual lifetime, returned from functions