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.
-std=c++17 flag.
Use modern C++ features: auto, range-based for loops, smart pointers (unique_ptr, shared_ptr),
and structured bindings where appropriate.
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
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)
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 forwardingParticleEffect- Complex class for testing move semantics and RAII behaviortest_scenarios.cpp- Validates exception handling, memory tracking, and singleton pattern- Expected Behavior: All tests should compile without warnings and demonstrate proper memory management
Detailed Requirements
Implement ALL of the following components. Each requirement maps to specific Module 7 topics.
Memory Pool Template Class
Implement MemoryPool<T> with:
- Template parameter
Tfor 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 newdeallocate(T* ptr)returning memory to free list- Move constructor and move assignment (no copy)
std::forward<Args>(args)... for perfect forwarding.
Use placement new: new (ptr) T(std::forward<Args>(args)...)
Custom Exception Classes
Create exception hierarchy:
MemoryException- Base class inheriting fromstd::exceptionPoolExhaustedException- 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
what().
Use std::string member to build detailed messages.
SmartHandle RAII Wrapper
Implement SmartHandle<T> with:
- Constructor accepting
T*andMemoryPool<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
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_mapof pools keyed bystd::type_index - Template method
getPool<T>(capacity)creating pool on first request - Thread-safe lazy initialization (use
std::call_onceor C++11 static magic)
std::type_index(typeid(T)) as map key.
C++11 guarantees thread-safe initialization of function-local statics.
Observer Pattern for Stats
Implement Observer pattern:
IMemoryObserverinterface with virtual methodsonAllocate()andonDeallocate()MemoryStatsclass implementing the interface- Track: total allocations, total deallocations, current usage, peak usage
MemoryPoolmaintains vector of observers- Notify all observers when allocate/deallocate occurs
- Provide
printReport()method in MemoryStats
std::unique_ptr<IMemoryObserver>
for automatic cleanup. Use range-based for loop to notify all observers.
SmartHandle Operators
Overload operators for SmartHandle<T>:
operator*- Dereference to returnT&operator->- Member access returningT*operator bool- Explicit conversion checking if ptr_ is not nulloperator==andoperator!=- Compare underlying pointers- All marked
constwhere appropriate
operator bool should be explicit to prevent
accidental implicit conversions. Return ptr_ != nullptr.
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
PoolExhaustedExceptionwhen 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;
}
Submission Guidelines
Create a public GitHub repository with the exact name shown below:
Required Repository Name
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)
valgrind --leak-check=full ./memory_pool_test
to ensure ZERO memory leaks. Include valgrind output in your README!
Enter your GitHub username - we'll verify your repository automatically
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 AssignmentSkills 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
Expert Tips
Templates Best Practices
- Define template classes entirely in headers (.hpp)
- Use
std::forwardfor perfect forwarding - Leverage
std::movewhen transferring ownership - Consider template specialization for edge cases
Exception Safety
- Use RAII to guarantee cleanup (SmartHandle)
- Mark move operations
noexceptwhen 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
= defaultfor 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)