Pointers/Memory Allocation   «Prev  Next»
Lesson 1

Pointers and Memory Allocation in C++

This module explores how C++'s use of pointers and memory allocation differs from C.
While pointers are used in much the same way in both languages, C++ introduces some interesting new features. In addition, C++ allows you to control the allocation and deallocation of a system-provided memory pool. This feature is particularly important for using dynamic data structures such as lists and trees.
As a C++ systems programmer, you have several language features that allow you to control the allocation and deallocation of memory, including system-provided memory pools[1]. These features provide fine-grained control over memory management, a critical aspect of system programming.
Key C++ Features for Memory Pool Management:
  1. Custom new and delete Operators:
    • You can overload the global or class-specific new and delete operators to allocate and deallocate memory from a custom memory pool instead of the default heap.
              void* operator new(size_t size) {
                // Custom memory pool allocation
                void* ptr = MyMemoryPool::allocate(size);
                return ptr;
              }
      
              void operator delete(void* ptr) {
                // Custom memory pool deallocation
                MyMemoryPool::deallocate(ptr);
              }
            
  2. Placement new:
    • The placement new operator allows you to construct objects at a specific memory location within your memory pool.
      char* buffer = new char[sizeof(MyClass)];
      MyClass* obj = new (buffer) MyClass(); // Constructs object in `buffer`
      obj->~MyClass(); // Manually call the destructor
      
  3. Memory Resource Classes (<memory_resource> in C++17):
    • C++17 introduced the <memory_resource> library, which provides std::pmr::memory_resource as an abstraction for custom memory pools. It allows you to create and manage memory pools flexibly.
    •         std::pmr::monotonic_buffer_resource pool{1024}; // 1KB buffer
              std::pmr::vector<int> vec(&pool); // Uses the memory pool
            
  4. Allocator Framework:
    • C++’s Standard Template Library (STL) supports custom allocators for containers, allowing you to direct allocations to a specific memory pool.
    •         template <typename T>
              class CustomAllocator {
              public:
                T* allocate(size_t n) {
                  return static_cast<T*>(MyMemoryPool::allocate(n * sizeof(T)));
                }
      
                void deallocate(T* ptr, size_t) {
                  MyMemoryPool::deallocate(ptr);
                }
              };
      
              std::vector<int, CustomAllocator<int>> vec;
            
  5. Low-Level Memory Functions (malloc, free, mmap):
    • You can directly use system-level APIs like malloc, free, or mmap to allocate large blocks of memory and subdivide them into smaller pools.
    •         void* pool = std::malloc(1024); // Allocate 1KB pool
              // Use the pool for custom allocations
              std::free(pool); // Free the entire pool
            
  6. Smart Pointers with Custom Deleters:
    • Use smart pointers like std::unique_ptr or std::shared_ptr with custom deleters to manage memory allocated from a memory pool.
    •         std::unique_ptr<int, void(*)(void*)> ptr(
                static_cast<int*>(MyMemoryPool::allocate(sizeof(int))),
                [](void* p) { MyMemoryPool::deallocate(p); }
              );
            
  7. RAII (Resource Acquisition Is Initialization):
    • C++’s RAII idiom ensures that allocated resources are automatically released when objects go out of scope. This is often used with custom classes managing memory pools.
    •         class MemoryPool {
              public:
                MemoryPool(size_t size) { /* Allocate pool */ }
                ~MemoryPool() { /* Deallocate pool */ }
              };
            
  8. Scoped Allocators (std::scoped_allocator_adaptor):
    • In cases of nested containers (e.g., std::vector<std::string>), std::scoped_allocator_adaptor ensures all allocations come from the same memory pool.
    •         using PoolAlloc = std::scoped_allocator_adaptor<CustomAllocator<int>>;
              std::vector<std::vector<int, PoolAlloc>, PoolAlloc> nested_vec(pool_alloc);
            
  9. Explicit Memory Control via System APIs:
    • In some cases, system-level APIs (e.g., VirtualAlloc on Windows, mmap on Unix) allow you to reserve and manage memory manually for the memory pool.
  10. Object Pools and Custom Allocators:
    • You can implement your own object pool to reuse allocated objects efficiently, avoiding frequent allocations and deallocations.
    •         class ObjectPool {
                std::vector<MyClass*> pool;
              public:
                MyClass* allocate() { /* Reuse or create new */ }
                void deallocate(MyClass* obj) { /* Return to pool */ }
              };
            
Summary:
These features give you precise control over memory usage, enabling efficient management of memory pools critical for performance-sensitive or resource-constrained applications. Among these, `new`/`delete` overloading, `std::pmr` resource management, and custom allocators are especially relevant for creating and managing system-provided memory pools in modern C++.

Big C++

Idea of a Pointer

C provides a remarkably useful type of variable called a pointer. A pointer is a variable that stores an address and its value is the address of another location in memory that can contain a value. You already used an address when you used the
  1. scanf() and
  2. scanf_s()
functions. A pointer variable with the name pNumber is defined by the second of the following two statements:
int Number = 25;
int *pNumber = &Number;

You declare a variable, Number, with the value 25, and a pointer, pNumber, which contains the address of Number. You can now use the variable pNumber in the expression *pNumber to obtain the value contained in Number. The * is the dereference operator, and its effect is to access the data stored at the address specified by a pointer.
This module discusses:
  1. How to create const pointer arguments to functions
  2. How to create aliases for variables using reference declarations
  3. How C++ implements call-by-reference using reference declaration
  4. How to use a generic pointer type
  5. How to use new and delete to manipulate free store memory
  6. How to create dynamically allocated multidimensional arrays
At the end of the module, you will be given the opportunity to take a quiz covering these topics.

[1]system-provided memory pool: In C++, a system-provided memory pool is a specialized mechanism offered by the underlying operating system or runtime environment to allocate and manage memory more efficiently than the standard malloc/free functions. While C++ itself doesn't have a built-in memory pool implementation, some operating systems and real-time environments often provide them as part of their standard library or specific system calls.

SEMrush Software