Неожиданные результаты при преобразовании строк wchar_t и c_str

Основываясь на этом ответе на связанный вопрос, я попытался написать метод, который преобразует стандартную строку в широкую строку, которая Затем я могу преобразовать в wchar_t*.

Почему два разных способа создания wchar_t* не эквивалентны? (Я показал значения, которые дает мне мой отладчик).

TEST_METHOD(TestingAssertsWithGetWideString)
{
   std::wstring wString1 = GetWideString("me");
   const wchar_t* wchar1 = wString1.c_str(); // wchar1 = "me"
   const wchar_t* wchar2 = GetWideString("me").c_str(); // wchar2 = "ﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮ@" (Why?!)
}

где GetWideString определяется следующим образом:

inline const std::wstring GetWideString(const std::string &str)
{
   std::wstring wstr;
   wstr.assign(str.begin(), str.end());

   return wstr;
};

Примечание: следующее тоже не работает.

const wchar_t* wchar2 = GetWChar("me");

const wchar_t *GetWChar(const std::string &str)
{
   std::wstring wstr;
   wstr.assign(str.begin(), str.end());

   return wstr.c_str();
};

person sgryzko    schedule 08.10.2013    source источник
comment
// Why doesn't this work?! - У вас есть указатель на временный буфер.   -  person chris    schedule 08.10.2013


Ответы (3)


Каждый раз, когда вы вызываете GetWideString(), вы создаете новый std::wstring, который имеет новый выделенный буфер памяти. Вы сравниваете указатели на разные блоки памяти (при условии, что Assert::AreEqual() просто сравнивает сами указатели, а не содержимое блоков памяти, на которые указывает указатель).

Обновление: const wchar_t* wchar2 = GetWideString("me").c_str(); не работает, поскольку GetWideString() возвращает временный std::wstring, который выходит за пределы области действия и освобождается, как только оператор завершается. Таким образом, вы получаете указатель на временный блок памяти, а затем оставляете этот указатель висящим, когда эта память освобождается, прежде чем вы сможете использовать указатель для чего-либо.

Кроме того, const wchar_t* wchar2 = GetWChar("me"); не должен компилироваться. GetWChar() возвращает std::wstring, который не реализует неявное преобразование в wchar_t*. Вы должны использовать метод c_str(), чтобы получить wchar_t* из std::wstring.

person Remy Lebeau    schedule 08.10.2013
comment
Хороший вопрос, но я отредактировал свой вопрос, чтобы уточнить, что я ищу. - person sgryzko; 08.10.2013
comment
Спасибо за объяснение. Я не понимал, что возвращаемый std::wstring выходит за рамки. (PS Я исправил ту часть, которую вы сказали не компилировать. Это была опечатка). - person sgryzko; 08.10.2013
comment
Я правильно понимаю, что std::string использует char? Как старый программист, пытающийся освоить новый способ ведения дел, у меня достаточно проблем с поиском оправдания для изучения std:string. Не говорите мне, что он не поддерживает широкие символы из коробки. - person Jonathan Wood; 19.03.2017
comment
@JonathanWood std::string использует char, да. std::wstring вместо этого использует wchar_t. А в C++11 теперь также есть std::u16string, использующий char16_t, и std::u32string, использующий char32_t. - person Remy Lebeau; 20.03.2017

Потому что два указателя не равны. wchar_t * не является String, поэтому вы получаете общий AreEqual.

person David Schwartz    schedule 08.10.2013
comment
Хороший вопрос, но я отредактировал свой вопрос, чтобы уточнить, что я ищу. - person sgryzko; 08.10.2013

std::wstring содержит расширенные символы типа wchar_t. std::string содержит символы типа char. Для специальных символов, хранящихся в std::string, используется многобайтовая кодировка, т. е. некоторые символы представляются двумя символами в такой строке. Таким образом, преобразование между ними не может быть таким простым, как вызов простого assign.

Для преобразования между «широкими» строками и многобайтовыми строками вы можете использовать следующие помощники (только для Windows):

// multi byte to wide char:
std::wstring s2ws(const std::string& str)
{
    int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
    std::wstring wstrTo(size_needed, 0);
    MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed);
    return wstrTo;
}

// wide char to multi byte:
std::string ws2s(const std::wstring& wstr)
{
    int size_needed = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), int(wstr.length() + 1), 0, 0, 0, 0); 
    std::string strTo(size_needed, 0);
    WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), int(wstr.length() + 1), &strTo[0], size_needed, 0, 0); 
    return strTo;
}
person LihO    schedule 08.10.2013
comment
Правильно ли предположить, что без каких-либо специальных символов мой простой assign будет работать как положено? - person sgryzko; 08.10.2013