Heute haben wir die Ursache eines bösen Fehlers herausgefunden, der nur zeitweise auf bestimmten Plattformen auftrat. In Kürze sah unser Code so aus:
class Foo {
map<string,string> m;
void A(const string& key) {
m.erase(key);
cout << "Erased: " << key; // oops
}
void B() {
while (!m.empty()) {
auto toDelete = m.begin();
A(toDelete->first);
}
}
}
Das Problem könnte in diesem vereinfachten Fall offensichtlich sein: B
Übergibt einen Verweis auf den Schlüssel an A
, der den Karteneintrag entfernt, bevor versucht wird, ihn zu drucken. (In unserem Fall wurde es nicht gedruckt, sondern komplizierter verwendet.) Dies ist natürlich undefiniertes Verhalten, da key
es sich nach dem Aufruf von um eine baumelnde Referenz handelt erase
.
Dies zu beheben war trivial - wir haben nur den Parametertyp von const string&
in geändert string
. Die Frage ist: Wie hätten wir diesen Fehler überhaupt vermeiden können? Anscheinend haben beide Funktionen das Richtige getan:
A
hat keine Ahnung, waskey
sich auf das bezieht, was zerstört werden soll.B
Sie hätten eine KopieA
erstellen können, bevor Sie an sie weitergeleitet wurden , aber ist es nicht die Aufgabe des Anrufers, zu entscheiden, ob Parameter nach Wert oder nach Referenz verwendet werden sollen?
Gibt es eine Regel, die wir nicht befolgt haben?