Deleting Raw Pointers from Unique Pointers
What happens if I try to delete the raw pointer obtained from the get()
method of a unique pointer?
Deleting a raw pointer obtained from the get()
method of a std::unique_ptr
is a dangerous operation that leads to undefined behavior. It's important to understand why this is problematic and how to avoid such situations.
Let's break this down with an example:
#include <iostream>
#include <memory>
class Character {
public:
Character(std::string name)
: name(std::move(name)) {
std::cout << "Creating " << name << '\n';
}
~Character() {
std::cout << "Destroying " << name << '\n';
}
private:
std::string name;
};
int main() {
auto frodo = std::make_unique<Character>("Frodo");
Character* rawPtr = frodo.get();
delete rawPtr;
// frodo still thinks it owns the object!
}
If we were to run this code, here's what would happen:
- The
Character
object "Frodo" is created and owned by thestd::unique_ptr
. - We get the raw pointer using
get()
. - We delete the raw pointer, which destroys the
Character
object. - At the end of
main()
,frodo
(thestd::unique_ptr
) goes out of scope and tries to delete the object again.
This results in a double deletion, which is undefined behavior. It could crash your program, corrupt memory, or seemingly do nothing while causing subtle bugs elsewhere.
Here's why this is dangerous:
- Ownership Violation:
std::unique_ptr
is designed to have exclusive ownership of the object it points to. By deleting the raw pointer, we're violating this contract. - Double Deletion: The
std::unique_ptr
will try to delete the object again when it goes out of scope, leading to undefined behavior. - Dangling Pointer: After deleting the raw pointer, the
std::unique_ptr
becomes a dangling pointer, pointing to memory that has been freed.
To avoid these issues:
- Never delete raw pointers obtained from
get()
. - Use
get()
only when you need to pass the raw pointer to functions that expect a raw pointer, and ensure those functions don't delete the pointer. - If you need to transfer ownership, use
std::move()
instead:
#include <iostream>
#include <memory>
class Character {
public:
Character(std::string name)
: mName(std::move(name)) {
std::cout << "Creating " << mName << '\n';
}
~Character() {
std::cout << "Destroying " << mName << '\n';
}
private:
std::string mName;
};
void takeOwnership(std::unique_ptr<Character> ptr) {
// This function now owns the Character object
std::cout << "Taking ownership\n";
}
int main() {
auto FrodoPtr = std::make_unique<Character>("Frodo");
takeOwnership(std::move(FrodoPtr));
if (!FrodoPtr) {
std::cout << "FrodoPtr is now null";
}
}
Creating Frodo
Taking ownership
Destroying Frodo
FrodoPtr is now null
In this safe version, we transfer ownership of the Character
object to the takeOwnership
function using std::move()
. The std::unique_ptr
in main()
becomes null after the move, preventing any double deletion issues.
Remember, the whole point of std::unique_ptr
is to manage the lifetime of dynamically allocated objects automatically. By manually deleting the raw pointer, we're defeating this purpose and introducing potential bugs. Stick to using the std::unique_ptr
interface, and let it handle the deletion for you.
Memory Ownership and Smart Pointers
Learn how to manage dynamic memory using unique pointers and the concept of memory ownership