Мне нужно вызвать функцию, параметры которой имеют строковый тип.
Я подумал, что std::string
— хороший выбор, потому что он предоставляет много интерфейса.
Но недавно я обнаружил, что копия произошла, когда я перешел от char *
к const std::string &
.
вот код:
#include <iostream>
#include <string>
using namespace std;
void test_string(const std::string & s) {
printf("%p\n", s.data());
}
int main() {
char aa[] = "asd";
test_string(aa);
printf("%p\n", aa);
}
Как показывает приведенный выше код, я могу передать std::string и char []
в свою функцию test_string
.
И мой тип аргумента const std::string &
, я думал, что копии не будет.
Но выходные данные показывают, что адрес другой, я думаю, что функция создает строку временной переменной, которая копируется из моего char []
один за другим. что не обязательно.
Это неправильное использование (перейдите от char[]
к std::string
)? Есть ли какой-нибудь хороший совет, чтобы избежать копирования?
У вас есть два разных «объекта»: во-первых, это массив aa
в функции main
; Тогда это полностью независимый (и временный) объект, на который s
ссылается в функции test_string
. Почему вы решили, что оба указателя будут одинаковыми?
«...думаю, что функция создает строку временной переменной,...» так оно и есть. Возможно, используйте std::string_view в качестве типа параметра — конструктор (4) будет тем, который вам нужен.
@Someprogrammerdude, я думаю, было бы здорово, если бы строка просто изменила указатель data() на адрес aa (стоимость копирования всего одного указателя)
@kevinh std::string
владеет памятью (содержащей строку), это может вызвать проблемы, если он выйдет за пределы области действия и попытается удалить память, за которую не несет ответственности. std::string_view
не владеет памятью.
Но это не всегда возможно... Речь идет скорее о владении данными. Если бы объект std::string
не имел полного и полного владения, как бы он смог самостоятельно управлять памятью для этих данных? Как он узнает, какие данные необходимо освободить, а какие нет? Что делать, если вы сделаете несколько копий? Что, если вы попытаетесь изменить строку, и она использует указатель на (постоянный) строковый литерал?
std::string
можно изменить. Его изменение было бы катастрофой, если бы у него не было копии данных.
aa
— это массив char
, а не std::string, поэтому временный объект std::string
— это конструктор, который передается по ссылке (как s
) в test_string
.
При создании этого временного объекта создается его копия (данные всегда принадлежат объекту std::string
).
Если вы измените aa
на std::string
, копирование не будет выполнено, и оно будет передано по ссылке.
Альтернативно измените test_string
, чтобы принять std::string_view. std::string_view
не создает копию при создании (это просто не владеющее представление существующей строки, которое может быть создано из массива char
так же, как и из другого std::string
).
Как и все типы представлений (которые являются облегченными), по умолчанию вам следует передавать std::string_view
по значению.
В этой статье это объясняется более подробно: Три причины передавать std::string_view по значению.
Итак, могу ли я сказать, что замена всех const std::string &
на const std::string_view &
— это простое и безопасное улучшение?
Вам даже не нужно использовать ссылку. string_view
очень легкий и обычно передается по значению. См. это о передаче типов представлений по значению. Добавил обновленную информацию об этом в ответ.
Я подумал, что std::string — хороший выбор, потому что он предоставляет множество интерфейсов.
Это хороший выбор, так почему бы вам не использовать его последовательно во всех случаях?
Запасная копия связана с тем, что вы используете два типа данных, поэтому за преобразование придется платить.
#include <iostream>
#include <string>
using namespace std;
void test_string(const std::string & s) {
printf("%p\n", s.data());
}
int main() {
std::string aa{"asd"};
test_string(aa);
printf("%p\n", aa.data());
}
https://godbolt.org/z/5PYzroMMP
Он по-прежнему будет создавать ненужные копии, рассмотрите возможность использования вместо них std::string_view
и std::print
. Примечание: избегайте использования using namespace std;
. Например: godbolt.org/z/efbe1aa1n
Да, когда
std::string
состоит изaa
(который не являетсяstd::string
), создается копия. Если вы сначала передадитеstd::string
(т. е. еслиaa
былоstd::string
), это будет сделано без копии.