#c #dangling-pointer
#c #висячий указатель
Вопрос:
Согласно статье (здесь и там), этот код является ошибочным примером использования после освобождения:
#include <iostream>
#include <string>
#include <string_view>
int main() {
std::string s = "Hellooooooooooooooo ";
std::string_view sv = s "Worldn";
std::cout << sv;
}
В статье указано, что string
s будет освобожден при string_view
использовании! Это противоречит моему опыту отладки. Но я прошу вас подтвердить / проверить / проверить это.
По моему опыту, переменные стека / области видимости освобождаются (вызов деструктора был бы гораздо более правильной формулировкой) при выходе из области видимости. Это означает, что в этом случае это произойдет ПОСЛЕ std::cout << sv;
Однако я никогда не использовал string_view
, поэтому я не знаю ни о какой внутренней механике этого объекта.
Если это действительно опасное поведение, не могли бы вы это объяснить? В противном случае я был бы рад прочитать подтверждение того, что деструкторы переменных области видимости вызываются только при выходе из текущей области видимости, естественно, или при возникновении исключения, прерывающего поток в текущей области.
РЕДАКТИРОВАТЬ: после первых двух ответов это действительно бесплатное использование.
Дополнительный вопрос: как вы думаете, мы могли бы добавить конструктор перемещения с ключевым словом delete в определение string_view, чтобы запретить это?
Комментарии:
1. Дело в том, что область действия неназванного временного объекта, созданного с
s "Worldn"
помощью, ограничена этим одним утверждением, а не концомmain
.2. Да,
string::operator string_view
это огромная ошибка. Почему это стало применимым к ссылкам на значения rvalue? Головы должны катиться.3. @n.m. да, именно, почему он принимает
rvalue
ссылку? Это то, о чем я задавался вопросом после интересных ответов… Как вы думаете, мы могли бы добавить конструктор перемещения с ключевым словом delete в определение string_view, чтобы предотвратить это?4. @StephaneRolland Я подозреваю, что причина в том, что
std::string_view
ожидается, что он будет использоваться в качестве параметра функции aa, чтобы временные значения могли быть переданы в функцию (например, с помощью const ref ). Очевидно, что линия жизни подходит для этого сценария.5. @Galik Да, это законный сценарий, я отказываюсь от своего возражения 😉 Но, возможно, тогда string_view следует использовать только как параметр функции.
Ответ №1:
Проблема с этим кодом…
std::string_view sv = s "Worldn";
… это sv
значение установлено не на s
, а на безымянное временное значение, созданное выражением s "worldn"
. Это временное значение уничтожается сразу после завершения всего выражения (с точкой с запятой).
Так что да, это ошибка типа «использовать после освобождения».
Если вы хотите продлить срок службы этого временного файла, вы должны присвоить его переменной, которая будет его поддерживать — например, новому std::string
объекту:
std::string sv = s "Worldn"; // copy the temporary to new storage in sv
A std::string_view
— это просто «просмотр» строки, это не строка сама по себе. Он действителен только до тех пор, пока строка, на которую он «смотрит», действительна.
Здесь тоже есть еще одна особенность. Вы также можете привязать временное к const
ссылке, которая продлевает срок службы временных файлов:
std::string constamp; sv = s "Worldn"; // just keep the temporary living
Почему разрешена инициализация a std::string_view
из временного?
Я не могу говорить за комитет по стандартам, но я подозреваю, что std::string_view
ожидается, что он будет использоваться в качестве параметра функции, чтобы временные значения могли быть переданы в функцию (например, с помощью const ref ). Очевидно, что для этого сценария время жизни подходит.
Если бы мы запретили инициализацию из временных, то основное использование std::string_view
было бы сведено на нет. Вы были бы вынуждены создать new std::string
(или привязку к const ref) перед вызовом функции, что делает процесс неудобным.
Комментарии:
1.
keep the temporary living
— это одна ужасная вещь2. Хорошо. Если бы я знал, что string_view принимает указатель / ссылку в качестве входных данных, я мог бы почувствовать опасность. Спасибо за объяснение: симптом средство устранения. Я согласен, что это сложно / подвержено ошибкам.
3. @Galik Как вы думаете, мы могли бы добавить конструктор перемещения с ключевым словом delete в определение string_view, чтобы запретить это?
4. @StephaneRolland Я добавил несколько комментариев в конце.
5. @StephaneRolland Нет, правильный путь очевиден, не допускайте неявного преобразования. Это обсуждалось 20 лет назад при преобразовании в
char*
, это буквально одна и та же проблема.
Ответ №2:
Результатом выражения s “Worldn”
является временный объект. Время жизни этого временного std::string
файла достаточно для инициализации sv
. Память, на которую sv
ссылается, освобождается сразу после ее инициализации (когда временный объект уничтожается).