【问题标题】:C++ delete operator on pointer, pointer not nulling指针上的C ++删除运算符,指针不为空
【发布时间】:2012-01-13 20:43:45
【问题描述】:

我正在尝试在 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 函数找到了正确的边缘对象并将其从适当的列表中删除,但删除操作符没有做我想做的事情。

感谢任何帮助。提前致谢。

【问题讨论】:

  • edge 似乎是一个局部变量,它在“空”后立即超出范围。你能从 FindEdge() 中获得对指针的引用吗?
  • 嗯,我不确定你的意思。这不是使用指针的意义吗,这样我就不必担心范围,因为它指向内存位置?你能举个例子说明你的意思吗?
  • int x = 5; int* xp1 = &amp;x; int* xp2 = xp1; xp2 = nullptr; 。在这段代码中,xp1 不会是空指针

标签: c++ pointers null-pointer delete-operator


【解决方案1】:

e5 是一个局部变量,不同于类中的edge

两者都可能指向内存中的同一个对象,并不意味着如果你让一个为空,另一个也会指向空。

考虑这个简单的例子,

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*&amp;,而不是使用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 中创建并保存在列表中。

使用ip1p2 的类似代码如下:

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

演示:http://ideone.com/ARiIl

还要注意这些:

//after p1 = nullptr
cout << *p2 << endl; //dangerous - undefined behaviour!
cout << *p1 << endl; //dangerous - undefined behaviour!

【讨论】:

  • 感谢您的帮助,我正在按照您在这里向我解释的内容进行操作,非常感谢。但是,我有一个问题,如果我像你建议的那样从 T* 切换到 T*& ,那么我不能返回空值,这对我来说是必要的。你对这个问题有什么建议吗? Vertex&lt;TVertex, TEdge, TWeight&gt;*&amp; Graph&lt;TVertex, TEdge, TWeight&gt;::InsertVertex(const TVertex&amp; label)它不会让我回nullptr说它不能在两者之间转换。
  • 您需要非常小心,以免在调用 RemoveEdge 后 e5 成为悬空引用。
【解决方案2】:

要解决函数外的e5更新问题,可以使用shared_ptrweak_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() );
    }
};

现在您可以像这样在 main 中编写您的部分:

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。

【讨论】:

  • 嗯我明白了,我认为它会使内存位置或其他东西无效。你能帮我解决问题吗?我将如何使 e5 从 RemoveEdge 函数中为空。
  • -1:RemoveEdge 确实从集合中删除了指针,因此没有任何内容可以为空。
  • 我看不到您的编辑将如何工作,而且我需要在 RemoveEdge 函数中删除它,而不是 FindEdge 函数...
  • 你是对的 .. 不知道我是如何错过 RemoveEdge 实际上在做正确的事情 .. 我会再次清理它。
  • @SaadImran。我使用了remove(edge),因为_EdgesEdgePtr(共享指针)的列表。但是,我看不到/更改 Source()Destination() 返回的定义 - 所以我给他们传递了一个原始指针 - 相当于他们在原始代码中收到的内容。您可能希望稍后更改它们以接受弱指针或共享指针。
【解决方案3】:

不要将在 main.cpp 中创建的指针设为空。你只是在你的 RemoveEdge 方法中清空一个指针。

如果您想将 main.cpp 中的指针设为空,例如可以将其传递给 RemoveEdge 函数,这样您就可以消除在列表中搜索的需要。

【讨论】:

    【解决方案4】:

    RemoveEdge 中的edgeMain.cpp 中的e5 是两个不同的局部变量。 为其中一个赋值不会改变另一个的值。

    【讨论】:

      【解决方案5】:

      FindEdge 返回列表中存在的指针的副本,您正在将其复制到指针的另一个副本中(RemoveEdge 中的edge)。所以当你将此设置为NULL 时,只有这个copy 被修改,list 中的指针不受影响。

      【讨论】:

        猜你喜欢
        • 2012-05-21
        • 1970-01-01
        • 2010-09-08
        • 2011-05-09
        • 2013-07-22
        • 2016-11-02
        • 1970-01-01
        • 2018-05-14
        • 1970-01-01
        相关资源
        最近更新 更多