stringstream, string и char * путаница преобразования



мой вопрос можно свести к тому, откуда возвращается строка stringstream.str().c_str() жить в памяти, и почему она не может быть назначен const char*?



этот пример кода объяснит это лучше, чем я могу



#include <string>
#include <sstream>
#include <iostream>

using namespace std;

int main()
{
stringstream ss("this is a stringn");

string str(ss.str());

const char* cstr1 = str.c_str();

const char* cstr2 = ss.str().c_str();

cout << cstr1 // Prints correctly
<< cstr2; // ERROR, prints out garbage

system("PAUSE");

return 0;
}


предположение о том, что stringstream.str().c_str() может быть назначено const char* привело к ошибке, которая заняла у меня некоторое время, чтобы выследить.



для бонусных очков, может кто-нибудь объяснить, почему замена cout заявление



cout << cstr            // Prints correctly
<< ss.str().c_str() // Prints correctly
<< cstr2; // Prints correctly (???)


печать строки правильно?



я компилирую в Visual Studio 2008.

544   5  

5 ответов:

stringstream.str() возвращает временный строковый объект, который уничтожается в конце полного выражения. Если вы получаете указатель на строку C от (stringstream.str().c_str()), он будет указывать на строку, которая удаляется там, где оператор заканчивается. Вот почему ваш код печатает мусор.

вы можете скопировать этот временный строковый объект в какой-то другой строковый объект и взять строку C из этого:

const std::string tmp = stringstream.str();
const char* cstr = tmp.c_str();

обратите внимание, что я сделал временную строку const, потому что любые изменения это может привести к его перераспределению и, таким образом, визуализации cstr недействительным. Поэтому безопаснее не хранить результат вызова в str() вообще и пользуйся cstr только до конца полного выражения:

use_c_str( stringstream.str().c_str() );

конечно, последнее может быть нелегко, и копирование может быть слишком дорогим. Вместо этого вы можете привязать временное к const ссылка. Это продлит его срок службы до срока службы ссылки:

{
  const std::string& tmp = stringstream.str();   
  const char* cstr = tmp.c_str();
}

ИМО это лучшее решение. К сожалению, это не очень хорошо известны.

то, что вы делаете, это создание временного. Это временное существует в области, определенной компилятором, так что оно достаточно долго, чтобы удовлетворить требования того, куда оно идет.

как только заявление const char* cstr2 = ss.str().c_str(); завершена, компилятор не видит причин держать временную строку вокруг, и она уничтожена, и, таким образом, ваш const char * указывает на свободную память.

ваше утверждение string str(ss.str()); означает, что временное используется в конструкторе для string переменная str что вы поместили в локальный стек, и это остается так долго, как вы ожидаете: до конца блока или функции, которую вы написали. Таким образом,const char * внутри все еще хорошая память, когда вы пытаетесь cout.

в этой строке:

const char* cstr2 = ss.str().c_str();

ss.str() составит скопировать содержимого stringstream. Когда вы звоните c_str() в той же строке вы будете ссылаться на законные данные, но после этой строки строка будет уничтожена, оставив ваш char* чтобы указать на неиспользуемую память.

объект std:: string, возвращаемый ss.ул () - это временный объект, который будет иметь время жизни ограничено выражением. Поэтому вы не можете присвоить указатель на временный объект без мусора.

теперь есть одно исключение: если вы используете ссылку const для получения временного объекта, законно использовать его для более широкого времени жизни. Например, вы должны сделать:

#include <string>
#include <sstream>
#include <iostream>

using namespace std;

int main()
{
    stringstream ss("this is a string\n");

    string str(ss.str());

    const char* cstr1 = str.c_str();

    const std::string& resultstr = ss.str();
    const char* cstr2 = resultstr.c_str();

    cout << cstr1       // Prints correctly
        << cstr2;       // No more error : cstr2 points to resultstr memory that is still alive as we used the const reference to keep it for a time.

    system("PAUSE");

    return 0;
}

таким образом, вы получаете строку в течение более длительного времени.

теперь вы должны знать что существует своего рода оптимизация, называемая RVO, которая говорит, что если компилятор видит инициализацию через вызов функции и эта функция возвращает временную, она не будет делать копию, а просто сделает назначенное значение временным. Таким образом, вам не нужно на самом деле использовать ссылку, это только если вы хотите быть уверены, что она не будет копировать, что это необходимо. Так делают:

 std::string resultstr = ss.str();
 const char* cstr2 = resultstr.c_str();

было бы лучше и проще.

The ss.str() временный уничтожается после инициализации cstr2 завершено. Поэтому, когда вы печатаете его с cout, c-строка, которая была связана с этим std::string временный уже давно разрушен, и поэтому вам повезет, если он выйдет из строя и утвердит, а не повезет, если он печатает мусор или действительно работает.

const char* cstr2 = ss.str().c_str();

C-строка где cstr1 указывает, однако, связан со строкой, которая все еще существует в то время вы делаете cout - так это правильно печатает результат.

в следующем коде, первый cstr правильно (я предполагаю, что это cstr1 в реальном коде?). Второй выводит c-строку, связанную с временным строковым объектом ss.str(). Объект уничтожается в конце вычисления полного выражения, в котором он появляется. Полное выражение - это весь cout << ... выражение-таким образом, пока выводится c-строка, связанный строковый объект все еще существует. Ибо cstr2 - это чистое зло, что он преуспевает. Он, скорее всего, внутренне выбирает то же место хранения для нового временного, которое он уже выбрал для временного, используемого для инициализации cstr2. Он также может разбиться.

cout << cstr            // Prints correctly
    << ss.str().c_str() // Prints correctly
    << cstr2;           // Prints correctly (???)

возвращение c_str() обычно просто указывает на внутренний строковый буфер-но это не требование. Строка может составлять буфер, если ее внутренняя реализация не является непрерывной, например (это вполне возможно , но в следующем стандарте C++ строки должны быть смежно хранится).

в GCC строки используют подсчет ссылок и копирование при записи. Таким образом, вы обнаружите, что следующее справедливо (по крайней мере, в моей версии GCC)

string a = "hello";
string b(a);
assert(a.c_str() == b.c_str());

две строки имеют один и тот же буфер здесь. В момент изменения одного из них буфер будет скопирован и каждый будет содержать свою отдельную копию. Однако другие строковые реализации делают все по-другому.

Comments

    Ничего не найдено.