Me acabo de dar cuenta de algo inquietante. Cada vez que escribo un método que acepta std::string
un paramater, me abro a un comportamiento indefinido.
Por ejemplo, esto ...
void myMethod(const std::string& s) {
/* Do something with s. */
}
... se puede llamar así ...
char* s = 0;
myMethod(s);
... y no hay nada que pueda hacer para evitarlo (que yo sepa).
Entonces mi pregunta es: ¿cómo se defiende alguien de esto?
El único enfoque que viene a la mente es escribir siempre dos versiones de cualquier método que acepte un std::string
parámetro, como este:
void myMethod(const std::string& s) {
/* Do something. */
}
void myMethod(char* s) {
if (s == 0) {
throw std::exception("Null passed.");
} else {
myMethod(string(s));
}
}
¿Es esta una solución común y / o aceptable?
EDITAR: Algunos han señalado que debería aceptar en const std::string& s
lugar de std::string s
como un parámetro. Estoy de acuerdo. Modifiqué la publicación. Sin embargo, no creo que eso cambie la respuesta.
c_str
propiedad del objeto de cadena ?char* s = 0
no está definido. Lo he visto al menos unos cientos de veces en mi vida (generalmente en forma dechar* s = NULL
). ¿Tiene una referencia para respaldar eso?std:string::string(char*)
constructorconst std::string&
para ese parámetro ...?)Respuestas:
No creo que debas protegerte. Es un comportamiento indefinido del lado de la persona que llama. No eres tú, es la persona que llama
std::string::string(nullptr)
, que es lo que no está permitido. El compilador permite que se compile, pero también permite que se compilen otros comportamientos indefinidos.La misma forma sería obtener "referencia nula":
El que desreferencia el puntero nulo está haciendo UB y es responsable de ello.
Además, no puede protegerse después de que haya ocurrido un comportamiento indefinido, porque el optimizador tiene todo el derecho de asumir que el comportamiento indefinido nunca había sucedido, por lo que las comprobaciones si
c_str()
son nulas pueden optimizarse.fuente
El siguiente código proporciona un error de compilación para un pase explícito
0
y un error en tiempo de ejecución para unchar*
valor con0
.Tenga en cuenta que no implica que normalmente deba hacerlo, pero sin duda puede haber casos en los que se justifique la protección contra el error de la persona que llama.
fuente
También me encontré con este problema hace unos años y, de hecho, me pareció muy aterrador. Puede suceder pasando un
nullptr
o pasando accidentalmente un int con valor 0. Es realmente absurdo:Sin embargo, al final esto solo me molestó un par de veces. Y cada vez causó un bloqueo inmediato al probar mi código. Por lo tanto, no se requerirán sesiones nocturnas para solucionarlo.
Creo que sobrecargar la función
const char*
es una buena idea.Desearía que fuera posible una mejor solución. Sin embargo, no lo hay.
fuente
foo(0)
y un error de compilación parafoo(1)
¿Qué tal cambiar la firma de su método a:
De esa manera, la persona que llama debe crear una cadena antes de llamarla, y la descuido que le preocupa causará problemas antes de que llegue a su método, lo que hace obvio, lo que otros han señalado, que es el error de la persona que llama, no el suyo.
fuente
const string& s
, lo olvidé en realidad. Pero aun así, ¿no sigo siendo vulnerable a un comportamiento indefinido? La persona que llama aún puede pasar un0
, ¿verdad?¿Qué tal si proporciona sobrecarga la toma un
int
parámetro?Ni siquiera tiene que definir la sobrecarga. Intentar llamar
myMethod(0)
provocará un error de enlazador.fuente
0
tiene unchar*
tipo.Su método en el primer bloque de código nunca se llamará si intenta llamarlo con un
(char *)0
. C ++ simplemente intentará crear una cadena y arrojará la excepción por usted. ¿Lo intentaste?¿Ver? No tienes nada de qué preocuparte.
Ahora, si desea captar esto con un poco más de gracia, simplemente no debería estar usando
char *
, entonces el problema no surgirá.fuente
std::string
va a las bibliotecas utilizadas por otros proyectos donde no soy la persona que llama. Estoy buscando una manera de manejar con gracia la situación e informar a la persona que llama (tal vez con una excepción) que pasó un argumento incorrecto sin bloquear el programa. (De acuerdo, claro, la persona que llama podría no manejar una excepción que lanzo y el programa se bloqueará de todos modos.)Si le preocupa que char * sea punteros potencialmente nulos (p. Ej., Retornos de API C externas), la respuesta es usar una versión const char * de la función en lugar de std :: string one. es decir
Por supuesto, también necesitará la versión std :: string si desea permitir que se usen.
En general, es mejor aislar las llamadas a API externas y argumentos de mariscal en valores válidos.
fuente