Mir ist gerade etwas Störendes aufgefallen. Jedes Mal, wenn ich eine Methode geschrieben habe, die a std::string
als Parameter akzeptiert , habe ich mich für undefiniertes Verhalten geöffnet.
Zum Beispiel ist dies ...
void myMethod(const std::string& s) {
/* Do something with s. */
}
... kann man so nennen ...
char* s = 0;
myMethod(s);
... und ich kann nichts dagegen tun (was mir bewusst ist).
Meine Frage lautet also: Wie verteidigt sich jemand dagegen?
Der einzige Ansatz, der in den Sinn kommt, besteht darin, immer zwei Versionen einer Methode zu schreiben, die einen std::string
als Parameter akzeptiert , wie folgt :
void myMethod(const std::string& s) {
/* Do something. */
}
void myMethod(char* s) {
if (s == 0) {
throw std::exception("Null passed.");
} else {
myMethod(string(s));
}
}
Ist das eine gängige und / oder akzeptable Lösung?
EDIT: Einige haben darauf hingewiesen, dass ich const std::string& s
statt std::string s
als Parameter akzeptieren sollte . Genau. Ich habe den Beitrag geändert. Ich denke nicht, dass dies die Antwort ändert.
c_str
Eigenschaft des Zeichenfolgenobjekts nicht überprüfen konnten ?char* s = 0
das undefiniert ist. Ich habe es mindestens ein paar hundert Mal in meinem Leben gesehen (normalerweise in Form vonchar* s = NULL
). Haben Sie eine Referenz, um das zu sichern?std:string::string(char*)
Konstrukteurconst std::string&
für diesen Parameter keine nehmen können ...?)Antworten:
Ich denke nicht, dass Sie sich schützen sollten. Es ist undefiniertes Verhalten auf der Seite des Anrufers. Sie sind es nicht, der anruft
std::string::string(nullptr)
, was nicht erlaubt ist. Der Compiler lässt es kompilieren, aber es können auch andere undefinierte Verhalten kompiliert werden.Der gleiche Weg wäre, "Null-Referenz" zu erhalten:
Derjenige, der den Nullzeiger dereferenziert, macht UB und ist dafür verantwortlich.
Darüber hinaus können Sie sich nicht schützen, nachdem ein undefiniertes Verhalten aufgetreten ist, da der Optimierer zu Recht davon ausgehen kann, dass das undefinierte Verhalten noch nie aufgetreten ist, sodass überprüft werden
c_str()
kann , ob null vorliegt.quelle
Der folgende Code gibt einen Kompilierungsfehler für einen expliziten Durchlauf
0
und einen Laufzeitfehler für einenchar*
mit Wert an0
.Beachten Sie, dass ich nicht impliziere, dass man dies normalerweise tun sollte, aber es kann zweifellos Fälle geben, in denen der Schutz vor dem Anruferfehler gerechtfertigt ist.
quelle
Ich bin auch vor ein paar Jahren auf dieses Problem gestoßen und fand es in der Tat sehr beängstigend. Dies kann passieren, indem Sie ein
nullptr
oder versehentlich ein int mit dem Wert 0 übergeben. Es ist wirklich absurd:Am Ende hat mich das jedoch nur ein paar Mal gestört. Und jedes Mal verursachte es einen sofortigen Absturz beim Testen meines Codes. Es sind also keine nächtlichen Sitzungen erforderlich, um das Problem zu beheben.
Ich halte eine Überlastung der Funktion für
const char*
eine gute Idee.Ich wünschte, eine schönere Lösung wäre möglich. Gibt es aber nicht.
quelle
foo(0)
und Kompilierungsfehler fürfoo(1)
Wie wäre es, wenn Sie die Signatur Ihrer Methode wie folgt ändern:
Auf diese Weise muss der Aufrufer eine Zeichenfolge erstellen, bevor er sie aufruft, und die Schlamperei, über die Sie sich Sorgen machen, führt zu Problemen, bevor Ihre Methode erreicht wird. Dies macht deutlich, was andere darauf hingewiesen haben, dass es sich um den Fehler des Aufrufers handelt, der nicht bei Ihnen liegt.
quelle
const string& s
, habe ich eigentlich vergessen. Aber bin ich trotzdem immer noch anfällig für undefiniertes Verhalten? Der Anrufer kann trotzdem ein0
, oder?Wie wäre es, wenn Sie bei Überlastung den Takes einen
int
Parameter bereitstellen ?Sie müssen nicht einmal die Überlast definieren. Der Versuch, einen Anruf zu
myMethod(0)
tätigen, löst einen Linker-Fehler aus.quelle
0
einenchar*
Typ hat.Ihre Methode im ersten Codeblock wird niemals aufgerufen, wenn Sie versuchen, sie mit einem aufzurufen
(char *)0
. C ++ versucht einfach, eine Zeichenfolge zu erstellen, und löst die Ausnahme für Sie aus. Hast Du es versucht?Sehen? Du musst dir keine Sorgen machen.
Wenn Sie dies jetzt etwas eleganter einfangen möchten, sollten Sie es einfach nicht verwenden
char *
, dann tritt das Problem nicht auf.quelle
std::string
geht in Bibliotheken, die von anderen Projekten verwendet werden, bei denen ich nicht der Aufrufer bin. Ich suche nach einer Möglichkeit, die Situation angemessen zu handhaben und den Anrufer (möglicherweise mit einer Ausnahme) darüber zu informieren, dass er ein falsches Argument übergeben hat, ohne das Programm zum Absturz zu bringen. (OK, zugegeben, der Aufrufer behandelt möglicherweise keine von mir ausgelöste Ausnahme und das Programm stürzt trotzdem ab.)Wenn Sie befürchten, dass char * möglicherweise Null-Zeiger sind (z. B. Rückgaben von externen C-APIs), verwenden Sie statt der std :: string-Version eine const char * -Version der Funktion. dh
Natürlich benötigen Sie auch die std :: string-Version, wenn Sie die Verwendung dieser zulassen möchten.
Im Allgemeinen ist es am besten, Aufrufe an externe APIs und Marshall-Argumente in gültige Werte zu isolieren.
quelle