Грабли с временными переменными
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
Это работает!
December 16th, 2008 at 3:31 am
Естественно, что после удаления временного буфера там хранится мусор. Отладчик, вероятно, его не очищает. Также релиз версия, насколько я знаю, тоже этого не делает.
December 16th, 2008 at 7:48 am
Действительно сейчас поэксперементировал, код const char * cp=str.ToUTF8().data(); – падает только в дебаге и только если проходить программу отладчиком. В релизе с отладчиком и просто в дебаг-версии работает, хм.
December 16th, 2008 at 11:40 am
По моему скромному опыту, для любых классов wxWidgets лучше соблюдать правило: если тебе еще понадобится то, что возвращает функция класса, присваивай это своей локальной переменной. Там сплошь и рядом используются умные указатели, и wxString str1 = str.ToUTF8() не копирует содержимое, а передает владение объектом. Так что потерь по производительности нет, код читается легче – что еще надо?
Сам с хитрой памятью wxWidgets столкнулся, работая с wxImage.
wxImage Image = Image1 не копирует картинку, и работа c Image и Image1 изменяет один и тот же объект. Для создания копии – отдельная функция wxImage::Copy()
И еще фокус, который вообще не знаю, как объяснить: работает программа, запускаем Task Manager, видим, что жрет она 5,5 Мб памяти. Сворачиваем основное окно – программа использует всего 500 Кб памяти! Разворачиваем – 1,5 Мб. По мере открывания диалогов вырастает до тех же 5,5 Мб… Может, это я не знаю, как вовремя память “подчищать”?
December 16th, 2008 at 12:33 pm
wxString str1 = str.ToUTF8() вообще не скомпилируется, ToUTF8() возвращает wxCharBuffer:)
По поводу “wxImage Image = Image1 не копирует картинку” дык это reference counting он доке описан, то есть ничего неожиданного тут нет, так и должно быть.
А фокус это особеность винды и с wx не связан, точно также у меня ведет программа написанная на МФЦ. Когда программу минимизируешь, винда часть памяти в убирает в своп, если я правильно понимаю, когда разворачиваешь – подгружает постепенно. Task Mаnаdger’u не стоит доверять:)
December 16th, 2008 at 3:14 pm
Решил переписать это дело немного, чтобы вместо
void foo(const char * data);
было
void foo(const std::string & data);
так должно быть надежнее
January 13th, 2009 at 8:39 pm
Переписываю свою библиотеку под виджеты. Чёрт те что творится с этими переменными 🙂