Я немного запутался в C-струнах и широких C-струнах. Ради этого вопроса предположим, что я использую Microsoft Visual Studio 2010 Professional. Пожалуйста, дайте мне знать, если какая-либо из моих данных неверна.
У меня есть структура с константным членом wchar_t *, который используется для хранения имени.
struct A
{
const wchar_t* name;
};
Когда я назначаю объекту ‘a’ имя так:
int main()
{
A a;
const wchar_t* w_name = L"Tom";
a.name = w_name;
return 0;
}
Это просто копирование адреса памяти, на который указывает w_name, в a.name. Теперь w_name и a.name являются указателями широких символов, которые указывают на один и тот же адрес в памяти.
Если я прав, то мне интересно, что делать с такой ситуацией. Я читаю в строке C из атрибута XML, используя tinyxml2.
tinyxml2::XMLElement* pElement;
// ...
const char* name = pElement->Attribute("name");
Получив строку C, я преобразую ее в строку широких символов следующим образом:
size_t newsize = strlen(name) + 1;
wchar_t * wcName = new wchar_t[newsize];
size_t convertedChars = 0;
mbstowcs_s(&convertedChars, wcName, newsize, name, _TRUNCATE);
a.name = wcName;
delete[] wcName;
Если я пока прав, то строка:
a.name = wcName;
просто копирует адрес памяти первого символа массива wcName в a.name. Тем не менее, я удаляю wcName сразу после назначения этого указателя, что делает его указателем на мусор.
Как я могу преобразовать мою строку C в строку C широких символов и затем присвоить ее a.name?
Самый простой подход — это, вероятно, поручить вам name
переменная с управлением памятью. Это, в свою очередь, легко сделать, объявив его
std::wstring name;
У этих ребят нет концепции независимого контента и мутаций объектов, то есть вы не можете создать отдельных персонажей. const
и делает весь объект const
будет препятствовать тому, чтобы это было назначено.
Вы Можно сделать это, используя std::wstring
не полагаясь на дополнительное временное преобразование буфера выделения и уничтожения. Не очень важно, если вы явно не обеспокоены фрагментацией кучи или в ограниченной системе (например, Windows Phone). Это займет немного настройки на лицевой стороне. Позвольте стандартной библиотеке управлять памятью для вас (с небольшим толчком).
class A
{
...
std::wstring a;
};// Convert the string (I'm assuming it is UTF8) to wide char
int wlen = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, NULL);
if (wlen > 0)
{
// reserve space. std::wstring gives us the terminator slot
// for free, so don't include that. MB2WC above returns the
// length *including* the terminator.
a.resize(wlen-1);
MultiByteToWideChar(CP_UTF8, 0, name, -1, &a[0], wlen);
}
else
{ // no conversion available/possible.
a.clear();
}
С полной стороны, вы Можно собрать TinyXML для использования стандартной библиотеки и std::string
скорее, чем char *
, который на самом деле не очень вам помогает, но может спасти вас тонна будущего strlen()
звонит позже.
Как вы правильно упомянули a.name
это просто указатель, который не предполагает выделенного хранилища строк. Вы должны управлять им вручную, используя new
или статический / ограниченный массив.
Чтобы избавиться от этих скучных вещей, просто используйте один из доступных классов строк: CStringW
из ATL (простой в использовании, но специфичный для MS) или std::wstring
из STL (стандарт C ++, но не так легко конвертировать из char*
):
#include <atlstr.h>
// Conversion ANSI -> Wide is automatic
const CStringW name(pElement->Attribute("name"));
К несчастью, std::wstring
использование с char*
это не так просто.
Смотрите функцию преобразования здесь: Как преобразовать std :: string в LPCWSTR в C ++ (Unicode)