Module 01 Solutions
Exercise 00: BraiiiiiiinnnzzzZ
Section titled “Exercise 00: BraiiiiiiinnnzzzZ”Stack vs heap allocation - newZombie() returns heap, randomChump() uses stack.
#ifndef ZOMBIE_HPP#define ZOMBIE_HPP
#include <string>
class Zombie {private: std::string _name;public: Zombie(std::string name); ~Zombie(); void announce() const;};
Zombie* newZombie(std::string name); // Heap - caller must deletevoid randomChump(std::string name); // Stack - auto cleanup
#endifExercise 01: Moar brainz!
Section titled “Exercise 01: Moar brainz!”Allocate N zombies in a single allocation using new Zombie[N].
Zombie.hpp
Section titled “Zombie.hpp”#ifndef ZOMBIE_HPP#define ZOMBIE_HPP
#include <string>
class Zombie {private: std::string _name;public: Zombie(); // Default constructor needed for array allocation ~Zombie(); void setName(std::string name); void announce() const;};
Zombie* zombieHorde(int N, std::string name);
#endifZombie.cpp
Section titled “Zombie.cpp”#include "Zombie.hpp"#include <iostream>
Zombie::Zombie() {}
Zombie::~Zombie() { std::cout << _name << " is dead." << std::endl;}
void Zombie::setName(std::string name) { _name = name;}
void Zombie::announce() const { std::cout << _name << ": BraiiiiiiinnnzzzZ..." << std::endl;}zombieHorde.cpp
Section titled “zombieHorde.cpp”#include "Zombie.hpp"
Zombie* zombieHorde(int N, std::string name) { if (N <= 0) return NULL;
// Single allocation for N zombies Zombie* horde = new Zombie[N];
// Initialize each zombie with the name for (int i = 0; i < N; i++) horde[i].setName(name);
return horde;}main.cpp
Section titled “main.cpp”#include "Zombie.hpp"
int main() { int N = 5; Zombie* horde = zombieHorde(N, "Walker");
for (int i = 0; i < N; i++) horde[i].announce();
delete[] horde; // MUST use delete[] for arrays return 0;}Key Points:
- Default constructor required for
new Zombie[N](array allocation) - Use
setName()to initialize after allocation - Return pointer to first zombie (array decays to pointer)
- Caller must use
delete[](notdelete) to free array
Exercise 02: HI THIS IS BRAIN
Section titled “Exercise 02: HI THIS IS BRAIN”Demonstrates references as aliases.
#include <iostream>#include <string>
int main() { std::string str = "HI THIS IS BRAIN"; std::string* stringPTR = &str; std::string& stringREF = str;
// All print the same address std::cout << &str << std::endl; std::cout << stringPTR << std::endl; std::cout << &stringREF << std::endl;
// All print the same value std::cout << str << std::endl; std::cout << *stringPTR << std::endl; std::cout << stringREF << std::endl;
return 0;}Exercise 03: Unnecessary Violence
Section titled “Exercise 03: Unnecessary Violence”When to use reference (HumanA) vs pointer (HumanB).
Weapon.hpp
Section titled “Weapon.hpp”#ifndef WEAPON_HPP#define WEAPON_HPP
#include <string>
class Weapon {private: std::string _type;public: Weapon(std::string type); ~Weapon();
const std::string& getType() const; // Returns const reference void setType(std::string type);};
#endifWeapon.cpp
Section titled “Weapon.cpp”#include "Weapon.hpp"
Weapon::Weapon(std::string type) : _type(type) {}
Weapon::~Weapon() {}
const std::string& Weapon::getType() const { return _type;}
void Weapon::setType(std::string type) { _type = type;}HumanA.hpp
Section titled “HumanA.hpp”#ifndef HUMANA_HPP#define HUMANA_HPP
#include <string>#include "Weapon.hpp"
class HumanA {private: std::string _name; Weapon& _weapon; // Reference: MUST have weapon, initialized in constructorpublic: HumanA(std::string name, Weapon& weapon); ~HumanA(); void attack() const;};
#endifHumanA.cpp
Section titled “HumanA.cpp”#include "HumanA.hpp"#include <iostream>
// Reference must be initialized in initializer listHumanA::HumanA(std::string name, Weapon& weapon) : _name(name), _weapon(weapon) {}
HumanA::~HumanA() {}
void HumanA::attack() const { std::cout << _name << " attacks with their " << _weapon.getType() << std::endl;}HumanB.hpp
Section titled “HumanB.hpp”#ifndef HUMANB_HPP#define HUMANB_HPP
#include <string>#include "Weapon.hpp"
class HumanB {private: std::string _name; Weapon* _weapon; // Pointer: might not have weapon (can be NULL)public: HumanB(std::string name); ~HumanB(); void setWeapon(Weapon& weapon); void attack() const;};
#endifHumanB.cpp
Section titled “HumanB.cpp”#include "HumanB.hpp"#include <iostream>
HumanB::HumanB(std::string name) : _name(name), _weapon(NULL) {}
HumanB::~HumanB() {}
void HumanB::setWeapon(Weapon& weapon) { _weapon = &weapon; // Store address of the weapon}
void HumanB::attack() const { if (_weapon) std::cout << _name << " attacks with their " << _weapon->getType() << std::endl; else std::cout << _name << " has no weapon to attack with!" << std::endl;}Key Points:
- HumanA uses reference (
Weapon&): Must always have a weapon, initialized at construction - HumanB uses pointer (
Weapon*): Weapon is optional, can be set later or never - Reference must be initialized in constructor’s initializer list (cannot be NULL)
- Pointer can be NULL and reassigned
getType()returnsconst std::string&to avoid copying and prevent modification- When
club.setType()is called, both humans see the change (same weapon object)
Exercise 04: Sed is for losers
Section titled “Exercise 04: Sed is for losers”Replace all occurrences of s1 with s2 without using std::string::replace.
main.cpp
Section titled “main.cpp”#include <iostream>#include <fstream>#include <string>
std::string replaceAll(const std::string& content, const std::string& s1, const std::string& s2) { if (s1.empty()) return content;
std::string result; std::size_t pos = 0; std::size_t found;
while ((found = content.find(s1, pos)) != std::string::npos) { // Append everything before the match result.append(content, pos, found - pos); // Append the replacement result.append(s2); // Move past the match pos = found + s1.length(); } // Append the remainder result.append(content, pos, std::string::npos);
return result;}
int main(int argc, char** argv) { if (argc != 4) { std::cerr << "Usage: " << argv[0] << " <filename> <s1> <s2>" << std::endl; return 1; }
std::string filename = argv[1]; std::string s1 = argv[2]; std::string s2 = argv[3];
// Open input file std::ifstream inFile(filename.c_str()); if (!inFile.is_open()) { std::cerr << "Error: Cannot open file '" << filename << "'" << std::endl; return 1; }
// Read entire file content std::string content; std::string line; bool first = true; while (std::getline(inFile, line)) { if (!first) content += '\n'; content += line; first = false; } inFile.close();
// Replace all occurrences std::string result = replaceAll(content, s1, s2);
// Write to output file std::ofstream outFile((filename + ".replace").c_str()); if (!outFile.is_open()) { std::cerr << "Error: Cannot create output file" << std::endl; return 1; } outFile << result; outFile.close();
return 0;}Key Points:
- FORBIDDEN:
std::string::replace()- usefind()+substr()/append()instead - Use
std::ifstreamfor reading,std::ofstreamfor writing - In C++98, use
.c_str()to convertstd::stringtoconst char*for file streams - Output file name is
<filename>.replace - Handle edge cases: empty s1 (would cause infinite loop), file not found
- The algorithm: find → append before match → append replacement → repeat
Exercise 05: Harl 2.0
Section titled “Exercise 05: Harl 2.0”Pointers to member functions - avoid if/else chains.
void Harl::complain(std::string level) { 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; } }}Exercise 06: Harl Filter
Section titled “Exercise 06: Harl Filter”Switch with fall-through for filtering.
int getLevel(const std::string& level) { std::string levels[4] = {"DEBUG", "INFO", "WARNING", "ERROR"}; for (int i = 0; i < 4; i++) if (level == levels[i]) return i; return -1;}
// In main:switch (getLevel(argv[1])) { case 0: harl.debug(); // Fall through case 1: harl.info(); // Fall through case 2: harl.warning(); // Fall through case 3: harl.error(); break; default: std::cout << "[ Probably complaining about insignificant problems ]" << std::endl;}