C++通过引用触发析构函数问题求助(附测试代码)
Hey there! Let's dig into this destructor issue you're hitting with references in C++. Since your code snippet cuts off at the constructor definition, I'll fill in the logical gaps based on common scenarios with reference-related destructor surprises, and walk through the most likely causes and fixes.
First, let's restate your partial code for clarity:
#include <windows.h> #include <iostream> #include <vector> #include <string> #include <utility> #include <type_traits> #include <sstream> struct Procedure { Procedure(HANDLE) { std::cout << "ctor w/connection: " << this << std::endl; } ~Procedure() { std::cout << "dtor: " << this << std::endl; } // Incomplete: likely copy/move constructor or assignment operator Procedure(Pr... };
Common Scenarios Causing Unexpected Destructor Calls
1. Non-const References Bound to Temporary Objects
If you're binding a non-const reference to a temporary Procedure object, the temporary gets destroyed immediately after the statement finishes—even if the reference is still in scope. This leads to a dangling reference and an unexpected destructor call.
Example of bad code:
void risky_func() { // Temporary object is created here, then destroyed right after this line Procedure& bad_ref = Procedure(GetCurrentProcess()); // bad_ref now points to a destroyed object; any use is undefined behavior }
Fix: Use a const reference (which extends the temporary's lifecycle to match the reference's) or create a named object instead:
void safe_func() { Procedure proc(GetCurrentProcess()); // Named object with proper lifecycle Procedure& ref = proc; // Reference binds to a valid, long-lived object // ref is safe to use here; proc destructs when the function exits }
2. References Pointing to Container Elements That Get Destroyed
If your Procedure objects live in a container like std::vector, operations like push_back, reserve, or erase can destroy existing elements (e.g., during reallocation). Any references to those elements become dangling, and you'll see unexpected destructor calls when the elements are destroyed.
Example of the issue:
std::vector<Procedure> procs; procs.emplace_back(GetCurrentProcess()); Procedure& elem_ref = procs[0]; // Reference to the first element procs.reserve(10); // Vector reallocates, destroying the original element // elem_ref now dangles; the destructor for the original object was called during reallocation
Fix: Reserve space upfront if you know the container size, or use pointers/smart pointers instead of references to avoid dangling when the container changes.
3. Missing or Incorrect Copy/Move Semantics
If you haven't explicitly defined copy/move constructors or assignment operators for Procedure, the compiler generates default versions. These defaults perform shallow copies, which can lead to double-destruction (e.g., if your class holds a HANDLE that gets copied instead of transferred).
Example of problematic default behavior:
// Compiler-generated copy constructor copies the HANDLE Procedure proc1(GetCurrentProcess()); Procedure proc2 = proc1; // Shallow copy: both proc1 and proc2 hold the same HANDLE // When proc1 destructs, it closes the HANDLE; proc2's destructor will operate on an invalid HANDLE
Fix: Explicitly delete copy operations (if your class shouldn't be copied) or implement proper move semantics to transfer ownership of resources:
struct Procedure { Procedure(HANDLE h) : handle(h) { std::cout << "ctor w/connection: " << this << std::endl; } ~Procedure() { if (handle != INVALID_HANDLE_VALUE) { CloseHandle(handle); } std::cout << "dtor: " << this << std::endl; } // Disable copying Procedure(const Procedure&) = delete; Procedure& operator=(const Procedure&) = delete; // Implement move semantics Procedure(Procedure&& other) noexcept : handle(other.handle) { other.handle = INVALID_HANDLE_VALUE; std::cout << "move ctor: " << this << std::endl; } Procedure& operator=(Procedure&& other) noexcept { if (this != &other) { if (handle != INVALID_HANDLE_VALUE) { CloseHandle(handle); } handle = other.handle; other.handle = INVALID_HANDLE_VALUE; std::cout << "move assign: " << this << std::endl; } return *this; } private: HANDLE handle = INVALID_HANDLE_VALUE; };
Next Steps to Debug
- Complete your class definition: Add all constructors, assignment operators, and member variables so you can track resource ownership.
- Log all constructor calls: Add output to copy/move constructors to see exactly when objects are created and copied/moved.
- Audit reference usage: Check every place you use a reference to a
Procedure—ensure it's not binding to a temporary or a container element that might be destroyed.
内容的提问来源于stack exchange,提问作者yo_gdg

