栈组(SetOfStacks)实现的内存泄漏检测与动态内存管理最佳实践
Hi there! Let's break down your questions about memory leak detection, dynamic memory management best practices, and also spot some issues in your SetOfStacks implementation.
高效检测内存泄漏的方法
Here are the most practical tools and techniques to catch memory leaks:
- Valgrind (Memcheck): A go-to tool for Linux and macOS. Compile your code with
-gfor debug symbols, then runvalgrind --leak-check=full ./your_program. It will report exactly where leaked memory was allocated and not freed. - AddressSanitizer (ASAN): Integrated into Clang and GCC. Compile with
-fsanitize=address -g—it runs alongside your program, detects leaks (and other memory issues like buffer overflows) in real time, and gives detailed error messages. - Visual Studio Memory Leak Detector: On Windows, enable it by including
<crtdbg.h>and adding_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);at the start ofmain(). It will print leak reports when your program exits. - Custom Tracking (Last Resort): Overload
operator newandoperator deleteto log allocations/deallocations, but this is more manual and less scalable than using dedicated tools.
确保动态内存管理正确的常用方法
These practices will help you avoid leaks and memory bugs in the first place:
- Use Smart Pointers: Replace raw pointers with
std::unique_ptr(for exclusive ownership) orstd::shared_ptr(for shared ownership). They automatically delete the underlying object when it's no longer needed, eliminating manualdeletecalls. - Follow RAII: Resource Acquisition Is Initialization—wrap every dynamic resource (memory, files, sockets) in an object whose destructor cleans up the resource. Standard containers like
std::vectororstd::unordered_mapalready do this for their internal storage. - Avoid Manual Memory Management: Whenever possible, use standard library containers instead of raw arrays or manually allocated objects. For example, instead of
new Stack<int>, let a container manage the stack instances. - Enable Compiler Warnings: Turn on
-Wall -Wextra(GCC/Clang) or equivalent in Visual Studio. Warnings will catch issues like uninitialized pointers, missingdeletecalls, and unsafe pointer usage. - Test Edge Cases: Make sure to test scenarios like emptying stacks, popping from specific stacks (
pop_at), and exception safety (what ifnewthrows an exception? Does your code avoid leaks?).
Issues in Your SetOfStacks Implementation
Looking at your code, there's a critical memory leak in the pop method:
void pop(void) { Stack<int> *current_stack = all_stacks[this->stack_index]; if (current_stack->getSize() > 0) { current_stack->pop(); } if (current_stack->getSize() == 0) { all_stacks.erase(this->stack_index); // Erases the entry from the map --this->stack_index; --this->stack_count; // ❌ Missing delete current_stack! The stack's memory is never freed. } }
When you erase the stack from all_stacks, you don't delete the underlying Stack<int> object—this leaves that memory allocated forever, causing a leak.
Another minor issue: pop_at doesn't handle stacks that become empty after popping. While this doesn't cause a leak (the stack will be deleted in the destructor), it leaves empty stacks in your map unnecessarily, wasting a small amount of memory.
Fix Suggestion: Use Smart Pointers
Replace your std::unordered_map<int, Stack<int> *> with std::vector<std::unique_ptr<Stack<int>>>. This way, when you remove an element from the vector, the unique_ptr automatically deletes the stack for you:
class SetOfStacks { private: std::vector<std::unique_ptr<Stack<int>>> all_stacks; int max_capacity; public: void push(int plate_number) { if (!all_stacks.empty() && all_stacks.back()->getSize() < max_capacity) { all_stacks.back()->push(plate_number); return; } all_stacks.emplace_back(std::make_unique<Stack<int>>()); all_stacks.back()->push(plate_number); } void pop(void) { if (all_stacks.empty()) return; all_stacks.back()->pop(); if (all_stacks.back()->getSize() == 0) { all_stacks.pop_back(); // Automatically deletes the empty stack } } int peek(void) { return all_stacks.back()->peek(); } void pop_at(int stack_id) { if (stack_id >= 0 && stack_id < static_cast<int>(all_stacks.size())) { all_stacks[stack_id]->pop(); // Optional: Remove empty stack if needed if (all_stacks[stack_id]->getSize() == 0) { all_stacks.erase(all_stacks.begin() + stack_id); } } else { std::cout << "栈ID范围为0到" << (all_stacks.size() - 1) << "\n"; } } int how_many_stacks(void) { return static_cast<int>(all_stacks.size()); } int stack_occupancy(void) { return all_stacks.back()->getSize(); } int which_stack(void) { return static_cast<int>(all_stacks.size()) - 1; } SetOfStacks(int capacity) : max_capacity(capacity) { all_stacks.emplace_back(std::make_unique<Stack<int>>()); } ~SetOfStacks() { // No need for manual deletion—vector and unique_ptr handle it! } };
This eliminates the need for manual delete calls entirely, making memory leaks impossible in this class.
内容的提问来源于stack exchange,提问作者ptorr

