Reputation: 735
I'm pretty new to shared pointers and am trying to delete a node from the graph. When I delete a node, the incoming and outgoing edges stored in that node will be deleted. However, I also need to delete the outgoing and incoming edges (which is the incoming and outgoing edges of the deleted node respectively) of the node that was previously connected to the deleted node - which I call the linking node.
The code below shows my Graph class declarations:
template <typename N, typename E> class Graph {
private:
struct Node;
struct Edge;
struct Node {
N val_;
int numEdges_;
int numIncomingEdges_;
std::set<std::shared_ptr<Edge>> edges_;
std::set<std::shared_ptr<Edge>> incomingEdges_;
Node() {}
Node(const N x) : val_{x} { numEdges_=0; }
void printNode(N n);
~Node();
void update();
};
struct Edge {
std::weak_ptr<Node> orig;
std::weak_ptr<Node> dest;
E val_;
Edge(std::shared_ptr<Node> o, std::shared_ptr<Node> d, E x);
Edge() {};
void printEdge();
~Edge();
};
..... Some code for node and edge iterators
private:
std::map< N, std::shared_ptr<Node> > nodes_;
};
For the class to delete the node:
template <typename N, typename E>
void Graph<N, E>::deleteNode(const N& node) noexcept {
auto findNode = nodes_.find(node);
if (findNode != nodes_.end()) {
// find the node which has incoming edges into the deleted node and delete its edges
for (auto edge: findNode->second->incomingEdges_) { // for each edge in node's incomingEdges_
// get the origin node of incoming edge to deleted node through dest.lock()
auto originNodeOfIncomingEdge = edge->dest.lock();
auto nodeVal1 = originNodeOfIncomingEdge->val_;
std::cout << "Print out value of origin node of incoming edge to deleted node: " << nodeVal1 << std::endl;
auto findLinkingNode1 = nodes_.find(nodeVal1);
std::cout << "size of edges_ in linking node before deleting its outgoing edge (which is the incoming edge of deleted node): " << findLinkingNode1->second->edges_.size() << std::endl;
auto findEdge = findLinkingNode1->second->edges_.find(edge);
findLinkingNode1->second->edges_.erase(findEdge);
std::cout << "size of edges_ in linking node after deleting its outgoing edge (which is the incoming edge of deleted node): " << findLinkingNode1->second->edges_.size() << std::endl;
--findLinkingNode1->second->numEdges_;
}
... similar code to above for deleting the node that has outgoing edges from deleted node going into it
findNode->second.reset(); // deletes managed object of the shared_ptr
nodes_.erase(findNode); // removes the node from the map container
}
}
So the main thing that is confusing me is this part, where I'm trying to delete the edge from the for-loop to delete the edge in the linked node.
auto findEdge = findLinkingNode1->second->edges_.find(edge);
findLinkingNode1->second->edges_.erase(findEdge);
But I keep getting errors related to shared_ptr. First is related to the pointer:
test8c(2863,0x7fff78df2000) malloc: *** error for object 0x7ffda2403350: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6
Previously, my code for this part is findLinkingNode1->second->edges_.erase(edge);
without the findEdge. I was able to compile without any errors, but the edge was not deleted.
Can someone guide me as to how can I delete the edge from edges_ successfully? edges_ is declared as std::set<std::shared_ptr<Edge>> edges_;
as shown in the Graph class.
Upvotes: 0
Views: 793
Reputation: 118352
The way things are structured, this is not very efficient. Your Node
s destructor will need to:
Iterate over each of the Edge
in the Node
being destroyed.
For each Edge
get()
the other Node
.
Find the same Edge
in that other Node
's list, and delete it.
If this is a frequent operation, I would suggest the following refactoring:
Your Edge
s hold shared_ptr
s to your Node
s.
Your Node
s hold weak_ptr
s to your Edge
s.
Therefore, to delete a Node
, you iterate over the node's Edge
s, and delete them. After all Edge
s get deleted, all shared_ptr
s to the Node
s go out of scope, and the node gets destroyed.
If this is impractical, a less drastic redesign would be:
Assign a unique identifier, of some kind, to each Edge
. A simple incrementing counter will do, and it might be possible to handle this in Edge
's constructor, automatically.
Refer to all of your Edge
s using their unique identifier, and instead of a std::set
of shared_ptr
s of Edge
s, replace it with a std::map
of identifiers, to the shared_ptr
for that Edge
. This will make it trivial to remove each Edge
from the other Node
, when destroying a particular Node
.
Rather than implementing a discrete identifier, with some care it might be possible to use raw pointers to the Edge
s, with std::less<Edge *>
as their strict weak ordering comparator, as an impromptu unique identifier for each edge.
Upvotes: 0