Грабли с временными переменными

December 15th, 2008 Begemot Posted in Программирование

Я уже как-то писал про "мистику" . Вот сегодня еще наткнулся на интересные грабли. Задача такая есть юникодная строка надо конвертить ее в UTF8, получить указатель на то что получилось и дальше обработать. Если писать так

void foo(const char * data)

{

   int len=strlen(data);

   //работаем с указателем data

}

wxString str(wxT("sometext"));

foo(str.ToUTF8().data());

Все работает отлично. Если попытатся это реализовать прямо в коде

wxString str(wxT("sometext"));

const char *  cp=str.ToUTF8().data();

int len=strlen(cp);

//работаем с указателем data

То начинает бардак. Оно не работает, cp указывает на мусор, но если поставить брейкпоинт на 3 строчку и посмотреть в watch окне значение str.ToUTF8(), то после этого все хорошо, и cp указывает куда надо и длина правильно считается. Мистика:)

Если я правильно понимаю то дело во временности буфера возвращяемого ToUTF8(). В первом случаем он видимо уничтожается после выхода из функции, поэтому в теле функции указательно возвразщаемый data() валиден. Во втором wxCharBuffer уничтожается сразу же (?) и указатель кривой, но дебагер его как-то возвращяет в жизни…. – кто силен в теории с++ подскажите в чем дело грамотно:)

Вообщем что бы обойти это приходится писать вот такой код:

wxString str(wxT("sometext"));

const wxCharBuffer tcb=str.ToUTF8();

const char *  cp=tcb.data();

int len=strlen(cp);

//работаем с указателем data

Это работает!

Related:

6 Responses to “Грабли с временными переменными”

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

  2. Действительно сейчас поэксперементировал, код const char * cp=str.ToUTF8().data(); – падает только в дебаге и только если проходить программу отладчиком. В релизе с отладчиком и просто в дебаг-версии работает, хм.

  3. По моему скромному опыту, для любых классов wxWidgets лучше соблюдать правило: если тебе еще понадобится то, что возвращает функция класса, присваивай это своей локальной переменной. Там сплошь и рядом используются умные указатели, и wxString str1 = str.ToUTF8() не копирует содержимое, а передает владение объектом. Так что потерь по производительности нет, код читается легче – что еще надо?

    Сам с хитрой памятью wxWidgets столкнулся, работая с wxImage.
    wxImage Image = Image1 не копирует картинку, и работа c Image и Image1 изменяет один и тот же объект. Для создания копии – отдельная функция wxImage::Copy()

    И еще фокус, который вообще не знаю, как объяснить: работает программа, запускаем Task Manager, видим, что жрет она 5,5 Мб памяти. Сворачиваем основное окно – программа использует всего 500 Кб памяти! Разворачиваем – 1,5 Мб. По мере открывания диалогов вырастает до тех же 5,5 Мб… Может, это я не знаю, как вовремя память “подчищать”?

  4. wxString str1 = str.ToUTF8() вообще не скомпилируется, ToUTF8() возвращает wxCharBuffer:)

    По поводу “wxImage Image = Image1 не копирует картинку” дык это reference counting он доке описан, то есть ничего неожиданного тут нет, так и должно быть.

    А фокус это особеность винды и с wx не связан, точно также у меня ведет программа написанная на МФЦ. Когда программу минимизируешь, винда часть памяти в убирает в своп, если я правильно понимаю, когда разворачиваешь – подгружает постепенно. Task Mаnаdger’u не стоит доверять:)

  5. Решил переписать это дело немного, чтобы вместо
    void foo(const char * data);
    было
    void foo(const std::string & data);

    так должно быть надежнее

  6. Переписываю свою библиотеку под виджеты. Чёрт те что творится с этими переменными 🙂