Инициализировать std:: string из возможно нулевого указателя char
инициализации std::string
С NULL
char указатель-это неопределенное поведение, я считаю. Итак, вот альтернативные версии конструктора, где mStdString
является переменной-членом типа std::string
:
void MyClass::MyClass(const char *cstr) :
mStdString( cstr ? cstr : "")
{}
void MyClass::MyClass(const char *cstr) :
mStdString(cstr ? std::string(cstr) : std::string())
{}
void MyClass::MyClass(const char *cstr)
{
if (cstr) mStdString = cstr;
// else keep default-constructed mStdString
}
Edit, объявление конструктора внутри class MyClass
:
MyClass(const char *cstr = NULL);
какой из них, или, возможно, что-то еще, является лучшим или наиболее правильным способом инициализации std::string
от возможно NULL
указатель, и почему? Это по-другому для разных Стандарты C++? Предположим, обычные флаги оптимизации сборки выпуска.
Я ищу ответ с объяснением того, почему путь-правильный путь, или ответ со ссылкой (Это также относится, если ответ "не имеет значения"), а не только личные мнения (но если вы должны, по крайней мере, сделать это просто комментарий).
4 ответов
последний глуп, потому что он не использует инициализацию, когда это возможно.
первые два полностью идентичны семантически (подумайте о c_str()
функция-член), поэтому предпочитайте первую версию, потому что она самая прямая и идиоматическая и самая простая для чтения.
(здесь б быть семантической разницей, если std::string
у constexpr
конструктор по умолчанию, но это не так. Тем не менее, это возможно это std::string()
разное от std::string("")
, но я не знаю никаких реализаций, которые делают это, так как это, похоже, не имеет большого смысла. С другой стороны, популярные оптимизации малых строк в настоящее время означают, что обе версии, вероятно,не выполнить любое динамическое распределение.)
обновление: как указывает @Jonathan, два строковых конструктора, вероятно, будут выполнять другой код, и если это имеет значение для вас (хотя это действительно не должно), Вы можете рассмотреть четвертый версия:
: cstr ? cstr : std::string()
читать и по умолчанию-построение.
второй обновления: но предпочитаю cstr ? cstr : ""
. Как вы можете видеть ниже, когда обе ветви называют то же самое конструктор, это может быть реализовано очень эффективно с использованием условных ходов и без ветвей. (Таким образом, две версии действительно генерируют различный код, но первая лучше.)
для хихиканья я запустил обе версии через Clang 3.3, с -O3
, для x86_64, на struct foo;
как ваш и функция foo bar(char const * p) { return p; }
:
конструктор по умолчанию (std::string()
):
.cfi_offset r14, -16
mov R14, RSI
mov RBX, RDI
test R14, R14
je .LBB0_2
mov RDI, R14
call strlen
mov RDI, RBX
mov RSI, R14
mov RDX, RAX
call _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcm
jmp .LBB0_3
.LBB0_2:
xorps XMM0, XMM0
movups XMMWORD PTR [RBX], XMM0
mov QWORD PTR [RBX + 16], 0
.LBB0_3:
mov RAX, RBX
add RSP, 8
pop RBX
pop R14
ret
конструктор пустых строк (""
):
.cfi_offset r14, -16
mov R14, RDI
mov EBX, .L.str
test RSI, RSI
cmovne RBX, RSI
mov RDI, RBX
call strlen
mov RDI, R14
mov RSI, RBX
mov RDX, RAX
call _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcm
mov RAX, R14
add RSP, 8
pop RBX
pop R14
ret
.L.str:
.zero 1
.size .L.str, 1
в моем случае даже казалось бы, что ""
создает лучше код: обе версии называют strlen
, но пустая строковая версия не использует никаких прыжков, только условные перемещения (так как вызывается тот же конструктор, только с двумя разными аргументами). Конечно, это совершенно бессмысленное, непереносимое и непередаваемое наблюдение, но оно просто показывает, что компилятор не всегда нуждается в такой помощи, как вы могли бы подумать. Просто напишите код, который выглядит лучше всего.
предполагая, что вы довольны cstr == NULL
поддавшись пустой mStdString
, Я думаю, что первый, вероятно, лучший.
если ничего другого, третий вариант, который вы предоставляете, не работает, если mStdString
is const
. Средний вариант выигрывает от" семантики перемещения " в C++11, но менее очевидно оптимален или разумен.
Итак, мой голос идет с первым вариантом.
во-первых, во-первых, вы правы, от http://www.cplusplus.com/reference/string/string/string/:
Если S является нулевым указателем, если n == npos или если диапазон,указанный [first, last), недопустим, это вызывает неопределенное поведение.
кроме того, это зависит от того, что означает нулевой указатель для вас. Я предполагаю, что это то же самое, что пустая строка для вас.
Я бы пошел с первым, потому что его я читал лучше. Первый решение и второе-одно и то же. Третий не работал бы, если бы ваша строка была const
.
хотя это может не быть ответом (особенно, когда вы сформулировали вопрос), но он слишком длинный, чтобы соответствовать комментарию и имеет код в нем, который не орк в комментариях. Я полностью ожидаю получить downvoted и должен удалить этот пост, но я чувствую себя вынужденным что - то сказать.
почему инициализация char *
быть NULL - и если да, не могли бы вы нажать его вызывающему абоненту, чтобы понять эту ситуацию - например, передать пустую строку или "unknown"
или "(null)"
as соответствующий.
другими словами, что-то вроде этого:
void MyClass::MyClass(const char *cstr)
{
assert(cstr != NULL); // or "throw cstr_must_not_be_null;" or some such.
mStdString = cstr;
}
(вероятно, есть какой-то умный способ сделать это в списке инициализаторов, но я не могу понять, как правильно это сделать).
Я для одного не заинтересован в NULL в качестве ввода строкового параметра любым другим способом, кроме "это действительно не существует" - и если это то, что вы на самом деле пытаетесь воспроизвести, то у вас должен быть boolean
сказать "не существует", или указатель на std::string
это может быть NULL, если строка отсутствует.