Reputation: 877
Just for studying purpose I'm coding binary search tree rotation now.
I normally use std::unique_ptr
but I used std::shared_ptr
this time
This works correctly:
// Node implementation
template <Containable T = int> struct Node {
T key_ = T{};
bool black_ = false; // red-black tree
std::shared_ptr<Node> left_;
std::shared_ptr<Node> right_;
};
// this is a protected member function of red-black tree class
// xp is parent node of x
void left_rotate(Node *xp, Node* x) {
assert(x);
auto y = x->right_;
x->right_ = y->left_;
std::shared_ptr<Node> x_ptr;
if (!xp) {
x_ptr = root_;
root_ = y;
} else if (x == xp->left_.get()) {
x_ptr = xp->left_;
xp->left_ = y;
} else {
x_ptr = xp->right_;
xp->right_ = y;
}
y->left_ = x_ptr;
}
This crashes:
void left_rotate(Node *xp, Node* x) {
assert(x);
auto y = x->right_;
x->right_ = y->left_;
std::shared_ptr<Node> x_ptr(x);
if (!xp) {
root_ = y;
} else if (x == xp->left_.get()) {
xp->left_ = y;
} else {
xp->right_ = y;
}
y->left_ = x_ptr;
}
cppreference says: Link
std::shared_ptr
is a smart pointer that retains shared ownership of an object through a pointer. Several shared_ptr objects may own the same object. The object is destroyed and its memory deallocated when either of the following happens:
- the last remaining shared_ptr owning the object is destroyed;
- the last remaining shared_ptr owning the object is assigned another pointer via operator= or reset().
To avoid destroying the node pointed to by x
before assigning, I created another std::shared_ptr<Node>
that owns *x
, but in the second implementation, the node object pointed by x
is already destroyed before y->left_ = x_ptr
is called. The node object is actually destroyed when one of root_ = y
, xp->left_ = y
and xp->right_ = y
is called.
There are clearly multiple std::shared_ptr
objects that own the same node object. root_
, xp->left_
or xp->right_
is clearly NOT the last remaining std::shared_ptr
owning the object. Why this happens?
Upvotes: 0
Views: 1995
Reputation: 12322
void left_rotate(Node *xp, Node* x) {
...
std::shared_ptr<Node> x_ptr(x);
...
When you create the shared_ptr
it takes over ownership of x
. The problem here is that it is not yours to give. Someone else, another shared_ptr
, already has ownership of the pointer. So some of the operations you do before y->left = xptr
will assign a new value to the shared_ptr
owning x
and that deletes the x
.
The problem is that you use raw pointers as arguments. Whenever you extract the pointer from a shared_ptr
be very careful about the lifetime of the object. The extracted raw pointer does not keep the object alive. Something that becomes exceedingly difficult to reason about with function calls. Easy to mess up as you experienced.
It's easily avoided by passing shared_ptr
as arguments because they will keep your objects alive:
void left_rotate(std::shared_ptr<NodeY> xp, std::shared_ptr<Node> x) {
PS: I hope your root_
is a shared_ptr
too.
Upvotes: 3