fe.dev
fe.dev

Reputation: 33

C++ memory leak example via "new"

I'm in the process of learning C++, and I would like to understand how new and delete work for primitive types, and if there is any automatic memory management at all when not using smart pointers.

I have written the following code that tries to trigger a memory leak by repeatedly allocating memory for 10'000 integers in the heap (that is, if I understood how new works).

#include <iostream>
#include <string>
#include <algorithm>

int* MemoryLeak(const int size) {
    std::cout << "Allocating space for " << size << " ints..." << std::endl;
    return new int[size];
}

int main() {
    std::string keepGoing = "y";
    while (keepGoing != "n") {       
        std::cout << "Leak memory? [y/n]" << std::endl; 
        
        std::cin >> keepGoing;
        std::transform(keepGoing.begin(), keepGoing.end(), keepGoing.begin(), ::tolower);
        
        if (keepGoing == "y") {
            MemoryLeak(10000);
        } else if (keepGoing != "n") {
            keepGoing = "y";
        }
    }
}

I don't have a corresponding delete call, so I would expect the memory footprint of my program to grow after each "y" I enter. However, memory footprint stays the same (according to my task manager). Why is that? And how would I have to modify my snippet in order to cause a leak?

Upvotes: 3

Views: 258

Answers (1)

Chris
Chris

Reputation: 36506

As noted in comments, your compiler and/or operating system may be optimizing away the problem entirely.

So let's do a few things differently:

  • Allocate bigger blocks of memory so that the memory leak, if it exists, is easier to "see."
  • Use the return of MemoryLeak and write to it.
#include <iostream>
#include <string>
#include <algorithm>

int* MemoryLeak(const int size) {
    std::cout << "Allocating space for " << size << " ints..." << std::endl;
    return new int[size];
}

int main() {
    std::string keepGoing = "y";
    while (keepGoing != "n") {       
        std::cout << "Leak memory? [y/n]" << std::endl; 
        
        std::cin >> keepGoing;
        std::transform(keepGoing.begin(), keepGoing.end(), keepGoing.begin(), ::tolower);
        
        if (keepGoing == "y") {
            int *x = MemoryLeak(10000000);
            for (std::size_t i = 0; i < 10000000; i++) 
                x[i] = i;
        } else if (keepGoing != "n") {
            keepGoing = "y";
        }
    }
}

If, as mentioned in comments, we use smart pointers, the memory usage of the program remains constant because when the std::unique_ptr<int[]> goes out of scope, it is destroyed and the memory deallocated.

#include <iostream>
#include <string>
#include <algorithm>
#include <memory>

std::unique_ptr<int[]> MemoryLeak(const int size) {
    std::cout << "Allocating space for " << size << " ints..." << std::endl;
    return std::make_unique<int[]>(size);
}

int main() {
    std::string keepGoing = "y";
    while (keepGoing != "n") {       
        std::cout << "Leak memory? [y/n]" << std::endl; 
        
        std::cin >> keepGoing;
        std::transform(keepGoing.begin(), keepGoing.end(), keepGoing.begin(), 
::tolower);
        
        if (keepGoing == "y") {
            auto x = MemoryLeak(10000000);
            for (std::size_t i = 0; i < 10000000; i++) 
                x[i] = i;
        } else if (keepGoing != "n") {
            keepGoing = "y";
        }
    }
}

Of course, in practice we would likely not create a unique pointer to an array but rather achieve that effect with a std::vector.

Upvotes: 3

Related Questions