2016-11-02 2 views
1

erlebe ich sehr seltsames Verhalten, die ich nicht erklären kann. Ich hoffe, jemand könnte etwas Licht darauf werfen.Einige Vektorelemente nicht ändern

-Code-Schnipsel zuerst:

class TContour { 
public: 
    typedef std::pair<int,int> TEdge; // an edge is defined by indices of vertices 
    typedef std::vector<TEdge> TEdges; 

    TEdges m_oEdges; 

    void splitEdge(int iEdgeIndex, int iMiddleVertexIndex) { 
    TEdge & oEdge = m_oEdges[iEdgeIndex]; 
    m_oEdges.push_back(TEdge(oEdge.first, iMiddleVertexIndex)); 
    oEdge = TEdge(oEdge.second, iMiddleVertexIndex);   // !!! THE PROBLEM 
    }; 

    void splitAllEdges(void) { 
    size_t iEdgesCnt = m_oEdges.size(); 
    for (int i=0; i<iEdgesCnt; ++i) { 
     int iSomeVertexIndex = 10000; // some new value, not actually important 
     splitEdge(i, iSomeVertexIndex); 
    } 
    }; 
}; 

Als ich splitAllEdges() aufrufen, werden die ursprünglichen Kanten geändert und neue Kanten hinzugefügt werden (was die Behältergröße zu verdoppeln). Alles wie erwartet, mit Ausnahme von 1 Originalkante, die sich nicht ändert. Sollte die von Interesse sein, sein Index ist 3 und Wert ist [1,242]. Alle anderen ursprünglichen Kanten ändern sich, aber diese bleibt unverändert. Hinzufügen von Debug-Druck bestätigt, dass die Kante mit einem anderen Wert geschrieben wird, aber m_oEdges Inhalt ändert sich nicht.

Ich habe eine einfache Abhilfe, die problematische Zeile mit m_oEdges[iEdgeIndex] = TEdge(oEdge.end, iMiddleVertexIndex); ersetzt das Problem nicht beheben. Obwohl meine Sorge ist, was ist die Ursache für das unerwartete Verhalten. Könnte das einen Compiler Fehler sein (also welche anderen Fragen muss ich erwarten?), Oder haben einige dumme Fehler in meinem Code, den ich übersehen?

/usr/bin/c++ --version 
c++ (Debian 4.9.2-10) 4.9.2 

Die Umstellung von C++ 98 auf C++ 11 hat nichts geändert.

+0

Verweise auf Vektorelement kann nach einem Aufruf für ungültig erklärt werden – themagicalyang

+1

OT push_back: Sind nicht die Mitglieder einer 'std :: pair' namens' first'/'second' (statt' Starten'/'Ende')? –

+0

@AdrianColomitchi Sicher sind sie, danke für Ihre Notiz. Der Code wurde korrigiert. – yman

Antwort

3

Sie einen ungültigen Verweis nach dem push_back Betrieb verwenden.

Dieses:

TEdge & oEdge = m_oEdges[iEdgeIndex]; 

erwirbt die Referenz. Dann folgt aus:

m_oEdges.push_back(TEdge(oEdge.start, iMiddleVertexIndex)); 

potenziell ändert die Größe den Vektor, und dabei verliert das oEdge Referenz. An diesem Punkt ist dies:

oEdge = TEdge(oEdge.end, iMiddleVertexIndex); 

ist nicht mehr definieren Verhalten, wie Sie eine dangling Referenz verwenden. Reuse den Index, die Referenz, wie zum Beispiel:

m_oEdges[iEdgeIndex] = TEdge(m_oEdges[iEdgeIndex].end, iMiddleVertexIndex); 
+0

Wow. Jetzt, wo du das sagst, ist es vollkommen logisch, ich schäme mich dafür, dass ich es selbst nicht bemerkt habe. Ich habe versucht, jemand anderem die Schuld zu geben. Ich dachte zuerst, dies sollte in der Dokumentation zu operator [] (http://www.cplusplus.com/reference/vector/vector/operator [] /) vermerkt werden. Aber es ist sicherlich irgendwo dokumentiert, und es ist definitiv Erfahrung mit den Std-Containern, die ich noch lernen muss. Nun, danke, dass du mir dabei geholfen hast! – yman

+1

@yman Trie [cppreference.com] (http://en.cppreference.com/w/cpp/container/vector) statt. Beispiel: 'push_back' ist dokumentiert mit:" Wenn die neue 'size()' größer als 'capacity() 'ist, werden alle Iteratoren und Referenzen (einschließlich des Iterators" past-the-end ") für ungültig erklärt -end-Iterator wird ungültig gemacht. " Ehrlich gesagt, ist es wohl die beste C++ Referenzseite im Internet, wird ständig gepflegt und verbessert und wird von Leuten gepflegt, die unglaublich vertraut mit den Standards sind. Definitiv ein dauerhaftes Lesezeichen in Ihrer Symbolleiste wert. Viel Glück. – WhozCraig

1

Andere die Entwertung der Referenz erwähnt haben, so dass ich nicht gehe mehr ins Detail, dass auf.

Wenn die Leistung kritisch ist, können Sie explizit genügend Speicherplatz im ursprünglichen Vektor für die neuen Kanten reservieren, bevor Sie mit dem Schleifen beginnen. Dies würde das Problem vermeiden, wäre aber technisch immer noch inkorrekt. Es würde funktionieren, aber immer noch gegen die Regeln. Eine sicherere, aber etwas langsamere Methode wäre es, den Vektor zu durchlaufen, vorhandene Kanten zu ändern und neue Kanten in einem neuen Vektor zu erzeugen (mit genügend Speicherplatz, der vorher für die Performance reserviert wurde) und dann am Ende den neuen Vektor anzuhängen zu dem bestehenden.

Der sicherste Weg (einschließlich vollständig Ausnahme sicher zu sein), um einen neuen Vektor zu erzeugen wäre, iterieren durch den Anfangsvektor (das Doppelte der Größe des Anfangsvektors Reservierung) (ohne seine Kanten zu verändern), Schieben zwei neue Kanten in den neuen Vektor für jede alte Kante, und dann direkt am Ende vector.swap() der alte Vektor mit dem neuen Vektor.

Ein großer positiver Nebeneffekt dieses letzten Ansatzes ist, dass Ihr Code entweder vollständig erfolgreich ist oder die ursprünglichen Kanten unverändert lässt. Es bewahrt die Integrität der Daten auch bei Katastrophen.

P.S. Ich bemerke, dass Sie tun:

TEdge(oEdge.first, iMiddleVertexIndex) 
TEdge(oEdge.second, iMiddleVertexIndex) 

Wenn der Rest des Codes empfindlich auf Orientierung klingeln Sie wahrscheinlich die Parameter für die zweite Kante umkehren wollen. dh:

TEdge(oEdge.first,  iMiddleVertexIndex) 
TEdge(iMiddleVertexIndex, oEdge.second  ) 
+0

Danke, dass Sie alternative Wege vorgeschlagen haben. Ich weiß nicht wirklich die endgültige Größe in meiner realen Anwendung, das Schnipsel wurde auf diese Weise vereinfacht. Eine doppelte Reservierung könnte daher möglicherweise verschwenderisch sein. Aber dein Standpunkt ist klar und gültig, ich mag es. P.S. Ringorientierung ist für mich kein Problem. – yman

Verwandte Themen