cpp
exercises
exercises.cpp⚙️cpp
/**
* Rule of 3/5/0, Move Semantics & RAII - Exercises
* Practice problems for resource management in C++
*/
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <cassert>
using std::cout;
using std::endl;
// ============================================================================
// EXERCISE 1: Implement Rule of Three
// ============================================================================
/**
* TODO: Fix this IntArray class to properly implement the Rule of Three.
* Currently, it has a memory management bug.
*
* Requirements:
* 1. Implement copy constructor (deep copy)
* 2. Implement copy assignment operator (deep copy)
* 3. Destructor is already provided
*
* Test: After fixing, the test should run without memory issues
*/
class IntArray {
int* data;
size_t size;
public:
IntArray(size_t n) : size(n) {
data = new int[size];
for (size_t i = 0; i < size; ++i) {
data[i] = 0;
}
}
~IntArray() {
delete[] data;
}
// TODO: Add copy constructor
// IntArray(const IntArray& other) { ... }
// TODO: Add copy assignment operator
// IntArray& operator=(const IntArray& other) { ... }
int& operator[](size_t index) { return data[index]; }
int operator[](size_t index) const { return data[index]; }
size_t getSize() const { return size; }
};
void testExercise1() {
cout << "=== EXERCISE 1: Rule of Three ===" << endl;
/*
IntArray arr1(5);
for (size_t i = 0; i < 5; ++i) arr1[i] = i * 10;
// Test copy constructor
IntArray arr2 = arr1;
arr2[0] = 999;
// Test copy assignment
IntArray arr3(3);
arr3 = arr1;
arr3[1] = 888;
// Verify independence (deep copy)
assert(arr1[0] == 0); // Should NOT be 999
assert(arr1[1] == 10); // Should NOT be 888
cout << "arr1[0] = " << arr1[0] << " (expected 0)" << endl;
cout << "arr2[0] = " << arr2[0] << " (expected 999)" << endl;
cout << "arr3[1] = " << arr3[1] << " (expected 888)" << endl;
cout << "Exercise 1 PASSED!" << endl;
*/
cout << "Uncomment test code after implementing Rule of Three" << endl;
}
// ============================================================================
// EXERCISE 2: Implement Rule of Five
// ============================================================================
/**
* TODO: Extend IntArray to implement the Rule of Five.
* Add move semantics to the class.
*
* Requirements:
* 1. Keep copy constructor and copy assignment
* 2. Add move constructor
* 3. Add move assignment operator
* 4. Both move operations should be noexcept
* 5. Moved-from object should be in a valid but empty state
*/
class IntArray5 {
int* data;
size_t size;
public:
IntArray5(size_t n) : size(n), data(n > 0 ? new int[n]() : nullptr) {}
~IntArray5() {
delete[] data;
}
// Copy constructor
IntArray5(const IntArray5& other) : size(other.size) {
data = new int[size];
for (size_t i = 0; i < size; ++i) {
data[i] = other.data[i];
}
}
// Copy assignment (copy-and-swap)
IntArray5& operator=(IntArray5 other) {
swap(*this, other);
return *this;
}
// TODO: Add move constructor
// IntArray5(IntArray5&& other) noexcept { ... }
// TODO: Add move assignment operator
// IntArray5& operator=(IntArray5&& other) noexcept { ... }
friend void swap(IntArray5& a, IntArray5& b) noexcept {
using std::swap;
swap(a.data, b.data);
swap(a.size, b.size);
}
int& operator[](size_t index) { return data[index]; }
size_t getSize() const { return size; }
bool isEmpty() const { return size == 0 || data == nullptr; }
};
void testExercise2() {
cout << "\n=== EXERCISE 2: Rule of Five ===" << endl;
/*
IntArray5 arr1(1000000); // Large array
arr1[0] = 42;
// Move constructor
IntArray5 arr2 = std::move(arr1);
assert(arr1.isEmpty()); // arr1 should be empty after move
assert(arr2[0] == 42);
// Move assignment
IntArray5 arr3(100);
arr3 = std::move(arr2);
assert(arr2.isEmpty()); // arr2 should be empty after move
assert(arr3[0] == 42);
cout << "Move constructor: PASSED" << endl;
cout << "Move assignment: PASSED" << endl;
cout << "Exercise 2 PASSED!" << endl;
*/
cout << "Uncomment test code after implementing move operations" << endl;
}
// ============================================================================
// EXERCISE 3: RAII File Wrapper
// ============================================================================
/**
* TODO: Create an RAII wrapper for file operations.
*
* Requirements:
* 1. Constructor opens file
* 2. Destructor closes file
* 3. Non-copyable (delete copy operations)
* 4. Movable (implement move operations)
* 5. Provide writeLine() method
* 6. Throw exception if file cannot be opened
*/
class SafeFile {
// TODO: Add file handle member
// FILE* file;
public:
// TODO: Constructor - open file
// SafeFile(const std::string& path, const char* mode) { ... }
// TODO: Destructor - close file
// ~SafeFile() { ... }
// TODO: Delete copy operations
// TODO: Implement move operations
// TODO: writeLine method
// void writeLine(const std::string& line) { ... }
// TODO: isOpen check
// bool isOpen() const { ... }
};
void testExercise3() {
cout << "\n=== EXERCISE 3: RAII File Wrapper ===" << endl;
/*
try {
{
SafeFile file("/tmp/exercise3.txt", "w");
file.writeLine("Hello RAII!");
file.writeLine("This file is safely managed.");
} // File automatically closed here
cout << "File written and closed successfully!" << endl;
// Move test
SafeFile f1("/tmp/exercise3_move.txt", "w");
SafeFile f2 = std::move(f1);
assert(!f1.isOpen()); // f1 should be empty
assert(f2.isOpen()); // f2 should have the file
f2.writeLine("Moved file handle");
cout << "Move semantics: PASSED" << endl;
cout << "Exercise 3 PASSED!" << endl;
} catch (const std::exception& e) {
cout << "Error: " << e.what() << endl;
}
*/
cout << "Uncomment test code after implementing SafeFile" << endl;
}
// ============================================================================
// EXERCISE 4: RAII Resource Manager with Callback
// ============================================================================
/**
* TODO: Create a ScopeGuard class that runs a cleanup function on destruction.
*
* Usage:
* ScopeGuard guard([]() { cout << "Cleanup!" << endl; });
* // ... do work ...
* // When guard goes out of scope, cleanup runs automatically
*
* Requirements:
* 1. Store cleanup function (use std::function<void()>)
* 2. Execute function in destructor
* 3. Non-copyable
* 4. Provide dismiss() method to prevent cleanup
*/
#include <functional>
class ScopeGuard {
// TODO: Add cleanup function member
// std::function<void()> cleanup;
// bool dismissed;
public:
// TODO: Constructor
// explicit ScopeGuard(std::function<void()> fn) { ... }
// TODO: Destructor
// ~ScopeGuard() { ... }
// TODO: Delete copy operations
// TODO: dismiss() method
// void dismiss() { ... }
};
void testExercise4() {
cout << "\n=== EXERCISE 4: Scope Guard ===" << endl;
/*
int value = 0;
// Test automatic cleanup
{
ScopeGuard guard([&value]() { value = 100; });
value = 50;
}
assert(value == 100);
cout << "Automatic cleanup: PASSED" << endl;
// Test dismiss
value = 0;
{
ScopeGuard guard([&value]() { value = 999; });
value = 50;
guard.dismiss();
}
assert(value == 50); // Cleanup should NOT have run
cout << "Dismiss: PASSED" << endl;
cout << "Exercise 4 PASSED!" << endl;
*/
cout << "Uncomment test code after implementing ScopeGuard" << endl;
}
// ============================================================================
// EXERCISE 5: Rule of Zero
// ============================================================================
/**
* TODO: Refactor this ResourceOwner class to use Rule of Zero.
* Replace raw pointers with smart pointers and standard containers.
* After refactoring, you should be able to delete all special members.
*
* Current class has memory leaks and copy issues!
*/
// BROKEN VERSION - DO NOT USE
class BrokenResourceOwner {
int* data;
char* name;
int* history;
size_t historySize;
public:
BrokenResourceOwner(int val, const char* n) {
data = new int(val);
name = new char[strlen(n) + 1];
strcpy(name, n);
history = new int[100];
historySize = 0;
}
// MEMORY LEAK: No destructor!
// COPY BUG: No copy operations!
void addToHistory(int val) {
if (historySize < 100) {
history[historySize++] = val;
}
}
};
// TODO: Implement this using Rule of Zero
class ResourceOwner {
// TODO: Use smart pointers and containers
// std::unique_ptr<int> data;
// std::string name;
// std::vector<int> history;
public:
// TODO: Implement constructor
// ResourceOwner(int val, const std::string& n) { ... }
// NO destructor, copy, or move operations needed!
// The compiler-generated defaults will work correctly.
// TODO: addToHistory method
// void addToHistory(int val) { ... }
// TODO: print method
// void print() const { ... }
};
void testExercise5() {
cout << "\n=== EXERCISE 5: Rule of Zero ===" << endl;
/*
ResourceOwner r1(42, "Resource1");
r1.addToHistory(1);
r1.addToHistory(2);
// Copy should work automatically (deep copy)
ResourceOwner r2 = r1;
r2.addToHistory(3);
// Move should work automatically
ResourceOwner r3 = std::move(r1);
r2.print();
r3.print();
cout << "Rule of Zero works!" << endl;
cout << "Exercise 5 PASSED!" << endl;
*/
cout << "Uncomment test code after implementing ResourceOwner" << endl;
}
// ============================================================================
// EXERCISE 6: Smart Pointer Practice
// ============================================================================
/**
* TODO: Complete these smart pointer exercises.
*/
class Widget {
int id;
static int counter;
public:
Widget() : id(++counter) {
cout << " Widget " << id << " created" << endl;
}
~Widget() {
cout << " Widget " << id << " destroyed" << endl;
}
int getId() const { return id; }
};
int Widget::counter = 0;
void testExercise6() {
cout << "\n=== EXERCISE 6: Smart Pointer Practice ===" << endl;
cout << "\n6a) unique_ptr basics:" << endl;
// TODO: Create a unique_ptr<Widget> using make_unique
// auto w1 = ???
// TODO: Transfer ownership to w2
// auto w2 = std::move(???);
// Verify w1 is null and w2 owns the widget
cout << "\n6b) shared_ptr reference counting:" << endl;
// TODO: Create shared_ptr and verify reference count
// auto s1 = std::make_shared<Widget>();
// auto s2 = s1;
// cout << "Reference count: " << s1.use_count() << endl;
cout << "\n6c) unique_ptr with array:" << endl;
// TODO: Create unique_ptr for array of 5 ints
// auto arr = std::make_unique<int[]>(5);
// arr[0] = 10;
cout << "\n6d) Custom deleter:" << endl;
// TODO: Create unique_ptr with custom deleter that prints message
cout << "Exercise 6: Complete the TODOs!" << endl;
}
// ============================================================================
// EXERCISE 7: Exception Safety with RAII
// ============================================================================
/**
* TODO: Make this function exception-safe using RAII.
* Currently, resources may leak if an exception is thrown.
*/
void brokenProcess() {
// PROBLEM: If processData throws, cleanup never happens!
// Allocate resources
int* data = new int[1000];
FILE* file = fopen("/tmp/data.txt", "w");
// Some processing that might throw
// processData(data, file); // Might throw!
// Cleanup (might never be reached!)
fclose(file);
delete[] data;
}
void testExercise7() {
cout << "\n=== EXERCISE 7: Exception Safety ===" << endl;
// TODO: Rewrite brokenProcess using RAII
/*
void safeProcess() {
// Use smart pointers and RAII wrappers
auto data = std::make_unique<int[]>(1000);
// Use SafeFile from Exercise 3 or similar
// SafeFile file("/tmp/data.txt", "w");
// Now this is safe - resources cleaned up even on exception
// processData(data.get(), file.get());
}
*/
cout << "Implement safeProcess using RAII!" << endl;
}
// ============================================================================
// EXERCISE 8: Move Semantics Optimization
// ============================================================================
/**
* TODO: Optimize this code using move semantics.
* Count the number of copies and moves.
*/
class HeavyObject {
std::vector<int> data;
static int copies;
static int moves;
public:
HeavyObject() : data(10000) {}
HeavyObject(const HeavyObject& other) : data(other.data) {
++copies;
cout << " COPY #" << copies << endl;
}
HeavyObject(HeavyObject&& other) noexcept : data(std::move(other.data)) {
++moves;
cout << " MOVE #" << moves << endl;
}
HeavyObject& operator=(const HeavyObject& other) {
data = other.data;
++copies;
return *this;
}
HeavyObject& operator=(HeavyObject&& other) noexcept {
data = std::move(other.data);
++moves;
return *this;
}
static void resetStats() { copies = moves = 0; }
static void printStats() {
cout << "Copies: " << copies << ", Moves: " << moves << endl;
}
};
int HeavyObject::copies = 0;
int HeavyObject::moves = 0;
void testExercise8() {
cout << "\n=== EXERCISE 8: Move Semantics Optimization ===" << endl;
cout << "\nUnoptimized version:" << endl;
HeavyObject::resetStats();
{
std::vector<HeavyObject> vec;
HeavyObject obj;
vec.push_back(obj); // TODO: How to optimize?
vec.push_back(obj);
}
HeavyObject::printStats();
// TODO: Optimize using std::move() and emplace_back()
/*
cout << "\nOptimized version:" << endl;
HeavyObject::resetStats();
{
std::vector<HeavyObject> vec;
vec.reserve(2); // Prevent reallocation
vec.emplace_back(); // Construct in place
vec.emplace_back();
}
HeavyObject::printStats();
*/
cout << "Goal: Reduce copies to 0!" << endl;
}
// ============================================================================
// MAIN
// ============================================================================
int main() {
cout << "╔══════════════════════════════════════════════════════════════╗" << endl;
cout << "║ RULE OF 3/5/0, MOVE SEMANTICS & RAII EXERCISES ║" << endl;
cout << "╚══════════════════════════════════════════════════════════════╝" << endl;
testExercise1();
testExercise2();
testExercise3();
testExercise4();
testExercise5();
testExercise6();
testExercise7();
testExercise8();
cout << "\n═══════════════════════════════════════════════════════════════" << endl;
cout << "Complete all exercises by implementing the TODO sections!" << endl;
cout << "Remember:" << endl;
cout << " - Rule of 3: Destructor, Copy Constructor, Copy Assignment" << endl;
cout << " - Rule of 5: Add Move Constructor, Move Assignment" << endl;
cout << " - Rule of 0: Use smart pointers and containers" << endl;
cout << " - RAII: Acquire in constructor, release in destructor" << endl;
return 0;
}