Оператор удаления C ++ для указателя, указатель не обнуляется - PullRequest
1 голос
/ 06 декабря 2011

Я пытаюсь реализовать ориентированный граф в C ++.Однако у меня возникают проблемы с моей функцией RemoveEdge, после того, как я вызываю функцию, и она использует оператор удаления для указателя и устанавливает указатель на 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

Итак, как вы видите, моя программа падает после того, как я удаляю ребро из графика, по какой-то причине она выводит not null после выполнения функции RemoveEdge.Я не уверен, почему это происходит, я использовал оператор удаления, а также явно обнулял указатель после.Что я здесь не так делаю?

И да, я уверен, что ребро найдено, функция FindEdge находит правильный объект ребра и удаляет его из соответствующих списков, но оператор удаления не выполняет то, что яхочу это сделать.

Ценю любую помощь.Заранее спасибо.

Ответы [ 5 ]

5 голосов
/ 06 декабря 2011

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*, если вы используете 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

Демонстрация: http://ideone.com/ARiIl

Также обратите внимание на это:

//after p1 = nullptr
cout << *p2 << endl; //dangerous - undefined behaviour!
cout << *p1 << endl; //dangerous - undefined behaviour!
1 голос
/ 06 декабря 2011

FindEdge возвращает копию указателя, присутствующего в списке, который вы копируете в другую копию указателя (edge в RemoveEdge). Таким образом, если вы установите NULL, будет изменена только эта копия , указатель в list не будет затронут.

1 голос
/ 06 декабря 2011

edge в RemoveEdge и e5 в Main.cpp - две разные локальные переменные. Присвоение значения одному из них не изменит значения другого.

1 голос
/ 06 декабря 2011

Вы не обнуляете указатель, созданный в вашем main.cpp.Вы просто обнуляете указатель в своем методе RemoveEdge.

Если вы хотите обнулить указатель в вашем main.cpp, вы можете, например, передать его в функцию RemoveEdge, и с этим вы можете устранить необходимость поискавнутри вашего списка.

1 голос
/ 06 декабря 2011

Для решения проблемы обновления 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.

...