Assignment 7-A

Advanced OOP Mastery Project

Build a comprehensive Smart Memory Pool system that demonstrates mastery of templates, exception handling, move semantics, design patterns (Singleton, Factory, Observer), and operator overloading. A real-world application combining all Module 7 advanced concepts.

8-12 hours
Advanced
300 Points
Submit Assignment
What You'll Master
  • Function & class templates
  • Custom exception handling
  • Move semantics & perfect forwarding
  • Design patterns (Singleton, Factory, Observer)
  • Operator overloading & RAII
Contents
01

Assignment Overview

In this assignment, you will build a Smart Memory Pool Management System - a high-performance memory allocator that demonstrates ALL advanced C++ OOP concepts from Module 7. This comprehensive project requires templates, exception handling, move semantics, design patterns, and operator overloading working together in a cohesive system.

C++17 Required: Your code must compile with -std=c++17 flag. Use modern C++ features: auto, range-based for loops, smart pointers (unique_ptr, shared_ptr), and structured bindings where appropriate.
Skills Applied: This assignment tests mastery of Templates (7.1), Exception Handling (7.2), Move Semantics (7.3), Design Patterns (7.4), and Operator Overloading (7.5) from Module 7.
Templates (7.1)

Function templates, class templates, template specialization, concepts (C++20 optional)

Exception Handling (7.2)

Custom exception classes, try-catch blocks, exception safety guarantees

Move Semantics (7.3)

Move constructors, move assignment, perfect forwarding, rvalue references

Design Patterns (7.4)

Singleton, Factory, Observer patterns, RAII with smart pointers

Operator Overloading (7.5)

Arithmetic operators, comparison operators, stream operators (<<, >>), subscript operator

Ready to submit? Already completed the assignment? Submit your work now!
Submit Now
02

The Scenario

High-Performance Gaming Engine

You're a C++ Systems Engineer at NexaGames Studio, working on their new game engine. The lead architect has assigned you this critical task:

"Our game engine creates and destroys thousands of objects per frame (particles, projectiles, enemies). Standard new/delete is too slow. We need a custom memory pool system with templates for different object types, exception-safe allocation, move semantics for zero-copy transfers, observer pattern for memory stats, and operator overloading for intuitive API. Can you architect this?"

Your Task

Design and implement a Smart Memory Pool System with the following components:

  • MemoryPool<T> - Template class for pooled allocation (7.1)
  • Custom Exceptions - PoolExhaustedException, InvalidOperationException (7.2)
  • SmartHandle<T> - Move-only RAII wrapper with move semantics (7.3)
  • PoolManager - Singleton managing multiple pools (7.4)
  • MemoryStats - Observer pattern for allocation tracking (7.4)
  • Operator Overloading - For intuitive handle dereferencing and comparison (7.5)
03

The Datasets

You will create TWO test data files to validate your memory pool system. Create these files exactly as shown below:

File 1: test_objects.h (Test Object Definitions)

// Test classes for memory pool validation
#ifndef TEST_OBJECTS_H
#define TEST_OBJECTS_H

#include <string>
#include <iostream>

// Simple POD-like object for basic testing
struct GameObject {
    int id;
    float x, y, z;
    std::string name;
    
    GameObject(int id = 0, float x = 0.0f, float y = 0.0f, float z = 0.0f, 
               const std::string& name = "Object")
        : id(id), x(x), y(y), z(z), name(name) {
        std::cout << "GameObject " << id << " constructed\n";
    }
    
    ~GameObject() {
        std::cout << "GameObject " << id << " destroyed\n";
    }
};

// Complex object with resource management
class ParticleEffect {
private:
    int* data;
    size_t size;
public:
    int effectId;
    
    ParticleEffect(int id, size_t count) 
        : effectId(id), size(count), data(new int[count]) {
        std::cout << "ParticleEffect " << id << " created (" << count << " particles)\n";
    }
    
    ~ParticleEffect() {
        delete[] data;
        std::cout << "ParticleEffect " << effectId << " destroyed\n";
    }
    
    // Move semantics required
    ParticleEffect(ParticleEffect&& other) noexcept 
        : effectId(other.effectId), size(other.size), data(other.data) {
        other.data = nullptr;
        other.size = 0;
    }
    
    ParticleEffect& operator=(ParticleEffect&& other) noexcept {
        if (this != &other) {
            delete[] data;
            data = other.data;
            size = other.size;
            effectId = other.effectId;
            other.data = nullptr;
            other.size = 0;
        }
        return *this;
    }
    
    // Delete copy operations
    ParticleEffect(const ParticleEffect&) = delete;
    ParticleEffect& operator=(const ParticleEffect&) = delete;
};

#endif // TEST_OBJECTS_H

File 2: test_scenarios.cpp (Test Cases)

// Comprehensive test scenarios for memory pool system
#include "MemoryPool.h"
#include "SmartHandle.h"
#include "PoolManager.h"
#include "test_objects.h"
#include <iostream>
#include <vector>

void testBasicAllocation() {
    std::cout << "\n=== Test 1: Basic Allocation ===";
    MemoryPool<GameObject> pool(10);
    
    auto obj1 = pool.allocate(1, 10.0f, 20.0f, 30.0f, "Player");
    auto obj2 = pool.allocate(2, 5.0f, 15.0f, 25.0f, "Enemy");
    
    std::cout << "Allocated: " << pool.getAllocated() << "/" << pool.getCapacity();
    
    pool.deallocate(obj1);
    pool.deallocate(obj2);
}

void testPoolExhaustion() {
    std::cout << "\n=== Test 2: Pool Exhaustion ===";
    MemoryPool<GameObject> pool(3);
    std::vector<GameObject*> objects;
    
    try {
        for (int i = 0; i < 5; i++) {  // Try to allocate more than capacity
            objects.push_back(pool.allocate(i, 0, 0, 0, "Test"));
        }
    } catch (const PoolExhaustedException& e) {
        std::cout << "Caught expected exception: " << e.what();
    }
    
    for (auto obj : objects) {
        pool.deallocate(obj);
    }
}

void testSmartHandles() {
    std::cout << "\n=== Test 3: RAII SmartHandle ===";
    MemoryPool<ParticleEffect> pool(5);
    
    {
        SmartHandle<ParticleEffect> handle1(&pool.allocate(1, 1000), &pool);
        SmartHandle<ParticleEffect> handle2 = std::move(handle1);  // Test move
        
        std::cout << "Effect ID: " << handle2->effectId;
        // Automatic deallocation when handle2 goes out of scope
    }
    
    std::cout << "After scope exit, available: " << pool.getAvailable();
}

void testSingletonManager() {
    std::cout << "\n=== Test 4: Singleton PoolManager ===";
    auto& manager = PoolManager::getInstance();
    
    auto& goPool = manager.getPool<GameObject>(20);
    auto& pePool = manager.getPool<ParticleEffect>(10);
    
    auto go = goPool.allocate(100, 1, 2, 3, "Managed");
    
    manager.printAllStats();
    
    goPool.deallocate(go);
}

int main() {
    testBasicAllocation();
    testPoolExhaustion();
    testSmartHandles();
    testSingletonManager();
    return 0;
}
Test Object Purposes
  • GameObject - Simple struct for testing basic pool operations and perfect forwarding
  • ParticleEffect - Complex class for testing move semantics and RAII behavior
  • test_scenarios.cpp - Validates exception handling, memory tracking, and singleton pattern
  • Expected Behavior: All tests should compile without warnings and demonstrate proper memory management
04

Detailed Requirements

Implement ALL of the following components. Each requirement maps to specific Module 7 topics.

1
Memory Pool Template Class

Implement MemoryPool<T> with:

  • Template parameter T for any object type
  • Constructor accepting capacity (number of objects)
  • Pre-allocate memory using std::vector<T*> or raw array
  • Maintain a free list of available slots
  • allocate(Args&&... args) using perfect forwarding and placement new
  • deallocate(T* ptr) returning memory to free list
  • Move constructor and move assignment (no copy)
Hint: Use std::forward<Args>(args)... for perfect forwarding. Use placement new: new (ptr) T(std::forward<Args>(args)...)
2
Custom Exception Classes

Create exception hierarchy:

  • MemoryException - Base class inheriting from std::exception
  • PoolExhaustedException - Thrown when allocate() fails (no free slots)
  • InvalidOperationException - Thrown for double-free or invalid pointer
  • Each must override what() with descriptive messages
  • Include context (pool capacity, current usage) in exception messages
Hint: Store error message in constructor, return it from what(). Use std::string member to build detailed messages.
3
SmartHandle RAII Wrapper

Implement SmartHandle<T> with:

  • Constructor accepting T* and MemoryPool<T>*
  • Destructor automatically calling pool_->deallocate(ptr_)
  • Move constructor transferring ownership (set source ptr_ to nullptr)
  • Move assignment with self-assignment check
  • Delete copy constructor and copy assignment (move-only type)
  • Mark move operations as noexcept
Hint: This is RAII (Resource Acquisition Is Initialization). The handle owns the object and automatically returns it to the pool in destructor.
4
Singleton PoolManager

Implement Singleton pattern for PoolManager:

  • Private constructor and destructor
  • Static getInstance() method returning single instance
  • Delete copy/move constructors and assignment operators
  • Maintain std::unordered_map of pools keyed by std::type_index
  • Template method getPool<T>(capacity) creating pool on first request
  • Thread-safe lazy initialization (use std::call_once or C++11 static magic)
Hint: Use std::type_index(typeid(T)) as map key. C++11 guarantees thread-safe initialization of function-local statics.
5
Observer Pattern for Stats

Implement Observer pattern:

  • IMemoryObserver interface with virtual methods onAllocate() and onDeallocate()
  • MemoryStats class implementing the interface
  • Track: total allocations, total deallocations, current usage, peak usage
  • MemoryPool maintains vector of observers
  • Notify all observers when allocate/deallocate occurs
  • Provide printReport() method in MemoryStats
Hint: Store observers as std::unique_ptr<IMemoryObserver> for automatic cleanup. Use range-based for loop to notify all observers.
6
SmartHandle Operators

Overload operators for SmartHandle<T>:

  • operator* - Dereference to return T&
  • operator-> - Member access returning T*
  • operator bool - Explicit conversion checking if ptr_ is not null
  • operator== and operator!= - Compare underlying pointers
  • All marked const where appropriate
Hint: operator bool should be explicit to prevent accidental implicit conversions. Return ptr_ != nullptr.
7
Comprehensive Main Function

Create main() demonstrating:

  • Creating pools for at least 2 different types (e.g., int, custom struct)
  • Allocating objects using perfect forwarding
  • Moving handles between variables (demonstrate move semantics)
  • Using overloaded operators (*, ->, bool)
  • Catching PoolExhaustedException when pool is full
  • Attaching MemoryStats observer and printing final report
  • Accessing singleton PoolManager from multiple places
// Example test case structure:
struct GameObject {
    int id;
    float x, y;
    GameObject(int i, float px, float py) : id(i), x(px), y(py) {}
};

int main() {
    auto& manager = PoolManager::getInstance();
    auto& pool = manager.getPool<GameObject>(10);
    
    // Attach observer
    auto stats = std::make_unique<MemoryStats>();
    pool.addObserver(std::move(stats));
    
    try {
        // Allocate with perfect forwarding
        auto handle = pool.allocate(1, 10.0f, 20.0f);
        
        // Use operators
        if (handle) {
            std::cout << "Object ID: " << handle->id << std::endl;
            (*handle).x += 5.0f;
        }
        
        // Move semantics
        auto handle2 = std::move(handle);
        // handle is now empty
        
    } catch (const PoolExhaustedException& e) {
        std::cerr << "Pool exhausted: " << e.what() << std::endl;
    }
    
    manager.printAllStats();
    return 0;
}
05

Submission Guidelines

Create a public GitHub repository with the exact name shown below:

Required Repository Name
smart-memory-pool-cpp
github.com/<your-username>/smart-memory-pool-cpp
Required File Structure
smart-memory-pool-cpp/
├── include/
│   ├── MemoryPool.hpp         # Template class definition
│   ├── SmartHandle.hpp        # RAII handle with operators
│   ├── PoolManager.hpp        # Singleton manager
│   ├── MemoryExceptions.hpp   # Custom exception classes
│   └── MemoryObserver.hpp     # Observer interface + MemoryStats
├── src/
│   ├── PoolManager.cpp        # Singleton implementation
│   └── main.cpp               # Comprehensive test cases
├── CMakeLists.txt             # CMake build configuration
├── Makefile                   # Alternative: Makefile (if not using CMake)
├── README.md                  # REQUIRED - see contents below
└── docs/
    └── design.md              # Optional: Design decisions document
Compilation Requirements

Your code MUST compile with:

# Using g++
g++ -std=c++17 -Wall -Wextra -O2 -Iinclude src/*.cpp -o memory_pool_test

# OR using CMake
mkdir build && cd build
cmake .. && make
README.md Must Include:
  • Your full name and submission date
  • Compilation instructions (g++ or CMake)
  • How to run the test program
  • Brief description of design choices for each component
  • Module 7 topics applied - list which requirement uses which topic (7.1-7.5)
  • Any challenges faced and solutions
  • Sample output from running your program
Do Include
  • All required header files (.hpp)
  • Implementation files (.cpp) where needed
  • CMakeLists.txt or Makefile
  • Comprehensive main() with test cases
  • Inline comments explaining complex logic
  • Proper header guards or #pragma once
  • README with compilation and usage instructions
Do Not Include
  • Compiled binaries or .o files (use .gitignore)
  • IDE-specific folders (.vscode, .idea, build/)
  • Any third-party libraries (STL only)
  • Code that doesn't compile with -std=c++17
  • Memory leaks (use valgrind to check)
  • Incomplete implementations (all 7 requirements must work)
Memory Safety: Run your program with valgrind --leak-check=full ./memory_pool_test to ensure ZERO memory leaks. Include valgrind output in your README!
Submit Your Assignment

Enter your GitHub username - we'll verify your repository automatically

06

Grading Rubric

Your assignment will be graded on the following criteria (Total: 300 points):

Component Topic Points Criteria
MemoryPool Template 7.1 50 Correct template implementation, perfect forwarding, placement new, free list management
Custom Exceptions 7.2 40 Exception hierarchy, descriptive messages, proper throwing and catching
SmartHandle RAII 7.3 60 Move constructor/assignment, Rule of Five compliance, automatic cleanup, noexcept correctness
Singleton Pattern 7.4 40 Thread-safe singleton, deleted copy/move, proper resource management
Observer Pattern 7.4 40 Interface design, observer registration, notification mechanism, statistics accuracy
Operator Overloading 7.5 30 *, ->, bool, ==, != operators correctly implemented and const-correct
Code Quality All 20 Comments, naming conventions, header guards, separation of interface/implementation
Testing & Documentation All 20 Comprehensive main(), README completeness, sample output, design explanations
Total 300
Bonus Points (Up to +30)
  • +10 points: Implement C++20 concepts for type constraints on MemoryPool
  • +10 points: Add thread-safety using std::mutex for concurrent allocations
  • +10 points: Implement Factory pattern for creating different pool types

Ready to Submit?

Make sure you have completed all requirements and reviewed the grading rubric above.

Submit Your Assignment
07

Skills You Will Master

Advanced Templates (7.1)

Class templates, variadic templates, perfect forwarding with std::forward, template type deduction

Exception Safety (7.2)

Custom exception hierarchies, exception specifications, RAII for exception safety, proper error handling

Move Semantics (7.3)

Rvalue references, move constructors/assignment, Rule of Five, std::move, perfect forwarding patterns

Design Patterns (7.4)

Singleton (thread-safe), Observer (subject/observer), RAII with smart pointers, proper resource management

Operator Overloading (7.5)

Dereference operators (*, ->), conversion operators (bool), comparison operators, const-correctness

Memory Management

Custom allocators, placement new, memory pools, zero-copy transfers, resource lifetime management

08

Expert Tips

Templates Best Practices
  • Define template classes entirely in headers (.hpp)
  • Use std::forward for perfect forwarding
  • Leverage std::move when transferring ownership
  • Consider template specialization for edge cases
Exception Safety
  • Use RAII to guarantee cleanup (SmartHandle)
  • Mark move operations noexcept when possible
  • Provide strong exception guarantee where feasible
  • Catch exceptions by const reference
Move Semantics
  • Always nullify source pointer after move
  • Check for self-assignment in move assignment
  • Use = default for trivial special members
  • Test with move-only types (unique_ptr)
Memory Management
  • Use placement new for pre-allocated memory
  • Call destructor explicitly before reusing memory
  • Test with valgrind for memory leaks
  • Use smart pointers (unique_ptr, shared_ptr)
09

Pre-Submission Checklist

Code Requirements
Repository & Documentation