我正在尝试用C ++实现有向图。但是,我在使用RemoveEdge函数时遇到问题,在调用函数后它使用指针上的delete运算符并将指针设置为nullptr
时,它不会在函数范围之外归零。
我不确定我是否已经清楚地说明了我的问题,但也许有些代码会有所帮助。
Graph.h
template<class TVertex, class TEdge, class TWeight>
class Graph
{
protected:
std::list<Vertex<TVertex, TEdge, TWeight>*>* _Vertices;
std::list<Edge<TVertex, TEdge, TWeight>*>* _Edges;
public:
Graph();
int TotalVertices();
int TotalEdges();
std::list<Vertex<TVertex, TEdge, TWeight>*>* Vertices();
std::list<Edge<TVertex, TEdge, TWeight>*>* Edges();
Vertex<TVertex, TEdge, TWeight>* FindVertex(const TVertex&);
Vertex<TVertex, TEdge, TWeight>* InsertVertex(const TVertex&);
void RemoveVertex(const TVertex&);
Edge<TVertex, TEdge, TWeight>* FindEdge(const TEdge&);
Edge<TVertex, TEdge, TWeight>* InsertEdge(const TVertex&, const TVertex&, const TEdge&, const TWeight&);
void RemoveEdge(const TEdge&);
};
Graph.FindEdge()
template<class TVertex, class TEdge, class TWeight>
Edge<TVertex, TEdge, TWeight>* Graph<TVertex, TEdge, TWeight>::FindEdge(const TEdge& label)
{
Edge<TVertex, TEdge, TWeight>* edge = nullptr;
std::list<Edge<TVertex, TEdge, TWeight>*>::iterator it;
for(it = this->_Edges->begin(); it != this->_Edges->end(); ++it)
{
if(label == (*it)->Label())
{
edge = *it;
break;
}
}
return edge;
}
Graph.RemoveEdge()
template<class TVertex, class TEdge, class TWeight>
void Graph<TVertex, TEdge, TWeight>::RemoveEdge(const TEdge& label)
{
Edge<TVertex, TEdge, TWeight>* edge = this->FindEdge(label);
if(edge == nullptr)
return;
this->_Edges->remove(edge);
edge->Source()->RemoveEdge(edge);
edge->Destination()->RemoveEdge(edge);
// Problem is here, why isn't this working like I think it should?
delete edge;
edge = nullptr;
}
Main.cpp的
// created graph
// added vertices
// added edges
Edge<string, string, int>* e5 = graph->InsertEdge("Oshawa", "Toronto", "E5", 5);
graph->RemoveEdge("E5");
cout << ((e5 == nullptr) ? "null" : "not null") << endl; // this outputs not null
因此,当我从图表中删除边缘后,您可以看到我的程序崩溃,由于某种原因,在执行RemoveEdge函数后它会输出not null
。我不确定为什么会发生这种情况,我已经使用了delete运算符,并且我之后也明确地将指针取消了。我在这里做错了什么?
是的,我确定找到了边缘,FindEdge函数找到了正确的边缘对象并将其从相应的列表中删除,但删除操作符并没有按照我的意愿去做。
感谢任何帮助。提前谢谢。
答案 0 :(得分:5)
e5
是一个局部变量,与类中的edge
不同。
两者都可能指向内存中的同一个对象,并不意味着如果你创建一个null,另一个也会指向null。
考虑这个简单的例子,
int i = 10;
int *p1 = &i;
int *p2 = p1;
//here p1 and p2 points to the same object in memory which is i
p1 = nullptr; //it makes only p1 point to null
//here only p2 points to i
cout << *p2 << endl; //ok
cout << *p1 << endl; //dangerous - undefined behaviour!
我希望这可以帮助您了解您的计划的行为!
但是,你可以做一件事。如果您使用T*
,则不会使用T*&
,而是获得预期的结果:
Edge<string, string, int>* & e5 = graph->InsertEdge("Oshawa", "Toronto", "E5", 5);
// ^ see this
graph->RemoveEdge("E5");
cout << ((e5 == nullptr) ? "null" : "not null") << endl;
它应该工作,因为现在e5
是对类中对象的引用,它不再是一个不同的对象,它更像是指针的别名您在InsertEdge
中创建并保存在列表中。
使用i
,p1
和p2
的类似代码将是:
int i = 10;
int *p1 = &i;
int* & p2 = p1; //now it is a reference to p1, i.e an alias of p1
//here p1 and p2 points to the same object in memory which is i
p1 = nullptr; //it makes only p1 point to null
//here both p1 and p2 points to null
if ( p1 == nullptr)
cout << "p1 points to null" << endl;
if ( p2 == nullptr)
cout << "p2 points to null" << endl;
if ( p1 == p2)
cout << "p1 and p2 are equal" << endl;
输出:
p1 points to null
p2 points to null
p1 and p2 are equal
另请注意:
//after p1 = nullptr
cout << *p2 << endl; //dangerous - undefined behaviour!
cout << *p1 << endl; //dangerous - undefined behaviour!
答案 1 :(得分:1)
要解决在功能之外更新e5的问题,您可以使用shared_ptr
和weak_ptr
:
template<class TVertex, class TEdge, class TWeight>
class Graph
{
typedef Vertex<TVertex,TEdge,TWeight> VextexType;
typedef Edge<TVertex,TEdge,TWeight> EdgeType;
typedef shared_ptr<VertexType> VertexPtr;
typedef shared_ptr<EdgeType> EdgePtr;
protected:
std::list< VertexPtr >* _Vertices;
std::list< EdgePtr >* _Edges;
public:
Graph();
int TotalVertices();
int TotalEdges();
std::list<VertexPtr>* Vertices();
std::list<EdgePtr>* Edges();
VertexPtr FindVertex(const TVertex&);
VertexPtr InsertVertex(const TVertex&);
void RemoveVertex(const TVertex&);
EdgePtr FindEdge(const TEdge&)
{
for( std::list<EdgePtr>::iterator it = this->_Edges->begin(); it != this->_Edges->end(); ++it)
{
if( label == (*it)->Label())
{
return *it;
}
}
return EdgePtr();
}
EdgePtr InsertEdge(const TVertex&, const TVertex&, const TEdge&, const TWeight&);
void RemoveEdge(const TEdge& label)
{
EdgePtr edge = this->FindEdge(label);
if(!edge)
return;
this->_Edges->remove(edge);
edge->Source()->RemoveEdge( edge.get() );
edge->Destination()->RemoveEdge( edge.get() );
}
};
现在你可以像这样编写你的主要部分:
weak_ptr<Edge<string, string, int> > e5( graph->InsertEdge("Oshawa", "Toronto", "E5", 5) );
graph->RemoveEdge("E5");
cout << (e5 ? "null" : "not null") << endl;
请注意,我们使用weak_ptr来存储返回值,而不是shared_ptr。
答案 2 :(得分:1)
您不会使您在main.cpp中创建的指针为空。您只是在RemoveEdge方法中将指针归零。
如果你想在main.cpp中使指针为空,你可以将它传递给RemoveEdge函数,这样就可以省去在列表中搜索的需要。
答案 3 :(得分:1)
edge
中的 RemoveEdge
和 Main.cpp 中的e5
是两个不同的局部变量。
为其中一个值分配值不会改变另一个值。
答案 4 :(得分:1)
FindEdge
返回列表中存在的指针的副本,您将复制到指针的另一个副本 {edge
中{ {1}})。因此,当您将此设置为RemoveEdge
时,仅修改此副本,NULL
中的指针不会受到影响。