Я только что понял, что-то беспокоит. Каждый раз, когда я писал метод, который принимает a std::string
в качестве параметра, я открывал себя для неопределенного поведения.
Например, это ...
void myMethod(const std::string& s) {
/* Do something with s. */
}
... можно назвать так ...
char* s = 0;
myMethod(s);
... и я ничего не могу сделать, чтобы предотвратить это (о чем я знаю).
Итак, мой вопрос: как кто-то защищает себя от этого?
Единственный подход, который приходит на ум, - это всегда писать две версии любого метода, который принимает std::string
в качестве параметра, например:
void myMethod(const std::string& s) {
/* Do something. */
}
void myMethod(char* s) {
if (s == 0) {
throw std::exception("Null passed.");
} else {
myMethod(string(s));
}
}
Это общее и / или приемлемое решение?
РЕДАКТИРОВАТЬ: Некоторые указали, что я должен принять, const std::string& s
а не std::string s
в качестве параметра. Я согласен. Я изменил пост. Я не думаю, что это меняет ответ, хотя.
c_str
свойство строкового объекта ?char* s = 0
это не определено. Я видел это по крайней мере несколько сотен раз в своей жизни (обычно в формеchar* s = NULL
). У вас есть ссылка, чтобы подтвердить это?std:string::string(char*)
конструкторconst std::string&
для этого параметра ...?)Ответы:
Я не думаю, что вы должны защищать себя. Это неопределенное поведение на стороне звонящего. Звонит не ты, а тот, кто не звонит
std::string::string(nullptr)
. Компилятор позволяет его компилировать, но он также позволяет компилировать и другие неопределенные варианты поведения.Точно так же можно получить «нулевую ссылку»:
Тот, кто разыменовывает нулевой указатель, создает UB и отвечает за него.
Более того, вы не можете защитить себя после того, как произошло неопределенное поведение, потому что оптимизатор имеет полное право предполагать, что неопределенное поведение никогда не происходило, поэтому проверки,
c_str()
равные нулю, могут быть оптимизированы.источник
Приведенный ниже код дает ошибку компиляции для явного прохода
0
и ошибку времени выполнения дляchar*
значения with0
.Обратите внимание, что я не имею в виду, что обычно это следует делать, но, без сомнения, могут быть случаи, когда защита от ошибки вызывающего абонента оправдана.
источник
Я также столкнулся с этой проблемой несколько лет назад, и я обнаружил, что это действительно очень страшная вещь. Это может произойти, если передать a
nullptr
или случайно передать int со значением 0. Это действительно абсурдно:Тем не менее, в конце концов это всего лишь пару раз. И каждый раз это вызывало немедленный сбой при тестировании моего кода. Так что никаких ночных сессий не потребуется, чтобы это исправить.
Я думаю, что перегрузка функции
const char*
- это хорошая идея.Хотелось бы, чтобы было лучше решение. Тем не менее, нет.
источник
foo(0)
и ошибка компиляции дляfoo(1)
Как насчет изменения подписи вашего метода на:
Таким образом, вызывающий объект должен создать строку перед тем, как ее вызвать, и небрежность, о которой вы беспокоитесь, вызовет проблемы, прежде чем он попадет в ваш метод, делая очевидным то, на что указали другие, что это ошибка вызывающего, а не ваша.
источник
const string& s
, я забыл на самом деле. Но даже так, разве я все еще не уязвим для неопределенного поведения? Абонент все еще может передать0
, верно?Как насчет того, чтобы обеспечить перегрузку принимает
int
параметр?Вам даже не нужно определять перегрузку. Попытка вызова
myMethod(0)
вызовет ошибку компоновщика.источник
0
имеетchar*
тип.Ваш метод в первом блоке кода никогда не будет вызван, если вы попытаетесь вызвать его с помощью
(char *)0
. C ++ просто попытается создать строку и выдаст исключение для вас. Вы пробовали это?Видеть? Вам не о чем беспокоиться.
Теперь, если вы хотите поймать это более изящно, просто не стоит использовать
char *
, тогда проблема не возникнет.источник
std::string
идет в библиотеки, используемые другими проектами, где я не вызывающий. Я ищу способ изящно справиться с ситуацией и сообщить звонящим (возможно, с исключением), что они передали неверный аргумент без сбоя программы. (Да, конечно, вызывающая сторона может не обработать исключение, которое я выбрасываю, и программа все равно будет аварийно завершена.)Если вы беспокоитесь о том, что char * может быть нулевым указателем (например, при возврате из внешних API C), ответом будет использование версии функции const char * вместо std :: string. т.е.
Конечно, вам также потребуется версия std :: string, если вы хотите разрешить их использование.
В общем, лучше всего изолировать вызовы внешних API и маршальные аргументы в допустимые значения.
источник