imho.ws |
![]() |
![]() |
![]() |
# 1 |
Junior Member
Регистрация: 13.05.2004
Сообщения: 128
![]() ![]() |
std::vector
возникли у меня проблемы с использованием класса vector из стандатрной библиотеки.
Компилятор MS Visual Studio .Net при выполнении следующего фрагмента: Код:
#include <stdlib.h> #include <iostream> #include <conio.h> using std::vector; using std::cout; using std::endl; struct TS{ char* str; int len; TS():str(0),len(0){} ~TS(){ free(str); str=0; } }; int main(){ TS s; vector<TS> vec; char* temp="This is string"; s.len=strlen(temp); s.str=static_cast<char*>(malloc(s.len+1)); strcpy(s.str,temp); cout<<s.str<<'\n'; vec.push_back(s); cout<<(s.str==0)<<'\t'<<s.str<<'\n'; TS& el=vec.back(); cout<<(el.str==s.str)<<'\t'<<el.str<<endl; s.str=0;//Чтобы деструктор не пытался освободить память повторно. return 0; } Код:
This is string 0 8RD 1 8RD Только вот не пойму, причём здесь деструктор может быть причастен, ведь переменная s и вектор vec ещё используются (не говоря уже об области видимости)? Более того, как для переменной s, так и для vec.back(), он явно не вызывался, т.к. s.str==vec.back().str!=0. Может, у кого идеи есть по поводу этой проблемы. P.s. Такая "ботва" была обнаружена при отладке написанной программы, где такой подход был использован для того, чтобы при вызове vec.clear() автоматически освобождалась занятая память и при этом избежать "ненужных" выделений памяти и копирования. Вопрос 2: Что делать? Изменять char* на string не хочется, ибо нужно будет очень много править (везде идёт работа с C-строками). Конечно, можно, убрать деструктор и перед вызовом vec.clear() производить "чистку" или ввести новый класс Код:
class myvector: public vector<TS>{ public: void clear(){ for (int i=0;i<size();i++) free((*this)[i].str); vector<TS>::clear(); } }; Последний раз редактировалось Crazy_kettle; 29.01.2006 в 22:55. |
![]() |
![]() |
# 2 |
Full Member
Регистрация: 16.10.2002
Адрес: ArchLinux, Internet
Сообщения: 557
![]() ![]() ![]() ![]() |
Во первых, на g++ 3.4.4 всё пашет
Во вторых, если где-то создаётся временный объект TS, например при выполенении этой строчки: vec.push_back(s); то деструктор может быть вызван им. Поэтомы для объектов с поинтерами всегда стоит писать operator= и copy constructor. Если написать их в private без имплементации, т.е. запретить, то компилятор даст ошибку в том месте где он хотел их использовать. В третьих, лучше пользоваться new и delete т.к. они вызывают контруктор/деструктор в отличии от malloc/free. В этом примере это не важно imho. Надеюсь помог Последний раз редактировалось Drakosha; 29.01.2006 в 09:40. |
![]() |
![]() |
# 3 |
Member
Регистрация: 10.03.2002
Адрес: Israel
Сообщения: 245
![]() ![]() |
2 Drakosha.
vec.push_back(s) не создает стековый обьект. push_back в параметре получает константный референс Код:
void push_back( const Type& _Val ); 2 Crazy_kettle. Что-то намешано у тебя и С и С++. Вектор на самом деле при удалении элемента вызывет для него деструктор. Попробуй хранить в векторе указатели на структуру TS, а не сами сами структуры, тогда сам сможешь управлять выделением и освобожденим пямяти. То есть что то типа Код:
struct TS{ char* str; int len; TS():str(0),len(0){} TS(const char* s) ///нормальный конструктор { len = strlen(s); str= new char[len+1]; strcpy(str,s); } ~TS(){ if(str) delete [] str; str=0; } }; int main () { vector<TS*> vec; ///Это слишком длинно///////// TS* pTS = new TS();//("This is string"); char* temp="This is string"; pTS->len = strlen(temp); pTS->str = new char [pTS->len+1]; strcpy(pTS->str,temp); /////Проще написать нормальный конструктор и сделать /////так TS* pTS = new TS("This is string"); cout<<pTS->str<<'\n'; vec.push_back(pTS); cout<<(pTS->str==0)<<'\t'<<pTS->str<<'\n'; //запомнить указатель TS* pTS1 = vec.back(); cout<<(pTS1->str==pTS->str)<<'\t'<<pTS1->str<<endl; ///освобождение пямяти//////////// //удалить указатель из вектора vec.pop_back(); /// еще можно так vec.erase(0) или vec.clear(); //удалить сам указатель delete pTS1 ; }
__________________
Best Regards Последний раз редактировалось alexey_ma; 29.01.2006 в 22:06. |
![]() |
![]() |
# 4 | |||||
Junior Member
Регистрация: 13.05.2004
Сообщения: 128
![]() ![]() |
Цитата:
![]() Цитата:
В проге структура используется только для инициализации и чтобы запихнуться в вектор. Цитата:
![]() Цитата:
Цитата:
З.Ы. Как я понял, изящней последнего решения (то, кот. с наследованием) в моём первом после ничего не придумано. Его я и реализовал. Последний раз редактировалось Crazy_kettle; 29.01.2006 в 23:03. |
|||||
![]() |
![]() |
# 5 | |||
Full Member
Регистрация: 16.10.2002
Адрес: ArchLinux, Internet
Сообщения: 557
![]() ![]() ![]() ![]() |
Цитата:
![]() Цитата:
Цитата:
Бо вторых, по моему правильное решение написать их все таки так как твоэ решени может упасть на каком-то другом сценарии - все так там возможна двойная деаллокация. |
|||
![]() |
![]() |
# 6 | ||
Member
Регистрация: 10.03.2002
Адрес: Israel
Сообщения: 245
![]() ![]() |
Цитата:
Цитата:
__________________
Best Regards |
||
![]() |
![]() |
# 7 | |
Junior Member
Регистрация: 19.04.2002
Адрес: Дом
Пол: Male
Сообщения: 187
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() |
Цитата:
Уважаемый, MS тут не причем. STL он и в Африке STL. И в доках четко и ясно написано что конструкторы копирования и операция присваивания для пользовательских классов при хранении их не по указателю необходимы.
__________________
Дураки не динозавры - они не вымрут... Последний раз редактировалось PSyton; 30.01.2006 в 13:32. |
|
![]() |
![]() |
# 8 | ||||||||
Junior Member
Регистрация: 13.05.2004
Сообщения: 128
![]() ![]() |
Цитата:
![]() ![]() ![]() [hr] Цитата:
Цитата:
Цитата:
Цитата:
![]() Не предусмотрел я то, что при реализации vector: ![]() Цитата:
[hr] Цитата:
Цитата:
Интересует меня другое. Я ведь всегда думал, что STL разрабатывалась/писалась так, чтобы обеспечивать максимальное быстродействие. Только вот зачем нужно создавать временный объект? (конечно, программистам MS виднее, но разработчики библиотеки, поставляемой с g++ 3.4.4, обошлись же без этого). И это, имхо, создание временного объекта может отнимать уйму времени (если этот объект очень большой). Не зря же объекты передаются по ссылке ?! |
||||||||
![]() |
![]() |
# 9 | ||
Member
Регистрация: 10.03.2002
Адрес: Israel
Сообщения: 245
![]() ![]() |
Цитата:
Цитата:
Код:
void push_back( const Type& _Val ); Eще раз : Cоздается два объекта, один обьявленная вами локальная структура и ее копия в векторе. Оба этих объекта из-за отсутствия конструктора копирования имеют указатель char* str содержащий один и тот же адрес (это легко проверить в дебагире). Локальная структура уничтожается когда она выходит из скопа. Для неё вызывается деструктор в котором освобождается указатель str. Копия этой структуры уничтожается тогда когда вектор выходит из скопа. Вектор при удалении вызывает деструкторы для хранящихся в нём объектов. Следовательно деструктор копии пытается тоже освободить указатель str. Поскольку адрес хранящийся в этом указателе уже освобожден после вызова деструктора локальной структуры то происходит нарушение защиты памяти, в просторечии креш. Если вы мне не верите то спросите любого програмиста хотя бы немного разбирающегося в С++ , он вам скажет то-же самое. А еще лучше, до того как начинаете писать код почитайте соответствующую литературу и ознакомтесь, наконец, с документацией на STL.
__________________
Best Regards Последний раз редактировалось alexey_ma; 30.01.2006 в 23:07. |
||
![]() |
![]() |
# 10 | |
Junior Member
Регистрация: 19.04.2002
Адрес: Дом
Пол: Male
Сообщения: 187
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() |
Цитата:
Да и что значит "я потеряю в скорости"? Все относительно... Если у тебя 100 объектов, то ты можешь начихать на это, поскольку при современных скоростях это тьфу. К тому же при написании больших проектов, далеко не все упирается в скорость, но также еще и в возможность поддежки и расширения. Таким образом, написак конструктор копирования и оператор присваивания для класса 1 раз ты можешь забыть об этолм и без оглядки на всякие там особенности писать новый код с использованием объектов этого класса. На то он и C++ и ООП в частности - никто снаружи не должен заботится о выделеии/перевыделении/очистке памяти под твою внутреннюю строку. А почему бы не использовать std::string вместо этой убогой структуры?
__________________
Дураки не динозавры - они не вымрут... |
|
![]() |
![]() |
# 11 | |
Full Member
Регистрация: 16.10.2002
Адрес: ArchLinux, Internet
Сообщения: 557
![]() ![]() ![]() ![]() |
Цитата:
2. В данном случае объект не выходит из скопа до печати на экран. Как вы обясните поведение этой программы а не теоретическое поведение STL (которое вы объясняете совершенно правильно). Я вижу только одно объяснение - где-то временный объект таки создаётся. Кроме как в коде STL я для этого мест не вижу. |
|
![]() |
![]() |
# 12 | |
Member
Регистрация: 10.03.2002
Адрес: Israel
Сообщения: 245
![]() ![]() |
Цитата:
Код:
void _Insert_n(iterator _Where, size_type _Count, const _Ty& _Val) { // insert _Count * _Val at _Where _Ty _Tmp = _Val; // in case _Val is in sequence size_type _Capacity = capacity(); if (_Count == 0) ; else if (max_size() - size() < _Count) _Xlen(); // result too long else if (_Capacity < size() + _Count) { // not enough room, reallocate _Capacity = max_size() - _Capacity / 2 < _Capacity ? 0 : _Capacity + _Capacity / 2; // try to grow by 50% if (_Capacity < size() + _Count) _Capacity = size() + _Count; pointer _Newvec = this->_Alval.allocate(_Capacity); pointer _Ptr = _Newvec; _TRY_BEGIN _Ptr = _Ucopy(_Myfirst, _ITER_BASE(_Where), _Newvec); // copy prefix _Ptr = _Ufill(_Ptr, _Count, _Tmp); // add new stuff _Ucopy(_ITER_BASE(_Where), _Mylast, _Ptr); // copy suffix _CATCH_ALL _Destroy(_Newvec, _Ptr); this->_Alval.deallocate(_Newvec, _Capacity); _RERAISE; _CATCH_END _Count += size(); if (_Myfirst != 0) { // destroy and deallocate old array _Destroy(_Myfirst, _Mylast); this->_Alval.deallocate(_Myfirst, _Myend - _Myfirst); } _Myend = _Newvec + _Capacity; _Mylast = _Newvec + _Count; _Myfirst = _Newvec; } else if ((size_type)(_Mylast - _ITER_BASE(_Where)) < _Count) { // new stuff spills off end _Ucopy(_ITER_BASE(_Where), _Mylast, _ITER_BASE(_Where) + _Count); // copy suffix _TRY_BEGIN _Ufill(_Mylast, _Count - (_Mylast - _ITER_BASE(_Where)), _Tmp); // insert new stuff off end _CATCH_ALL _Destroy(_ITER_BASE(_Where) + _Count, _Mylast + _Count); _RERAISE; _CATCH_END _Mylast += _Count; fill(_ITER_BASE(_Where), _Mylast - _Count, _Tmp); // insert up to old end } else { // new stuff can all be assigned pointer _Oldend = _Mylast; _Mylast = _Ucopy(_Oldend - _Count, _Oldend, _Mylast); // copy suffix copy_backward(_ITER_BASE(_Where), _Oldend - _Count, _Oldend); // copy hole fill(_ITER_BASE(_Where), _ITER_BASE(_Where) + _Count, _Tmp); // insert into hole } }
__________________
Best Regards Последний раз редактировалось alexey_ma; 31.01.2006 в 11:54. |
|
![]() |
![]() |
# 14 | |||||||
Junior Member
Регистрация: 13.05.2004
Сообщения: 128
![]() ![]() |
alexey_ma вы наверное очень умный, но, имхо, это не значит, что это даёт Вам право [внимательно\вовсе] НЕ читать посты других участников.
Цитата:
Цитата:
Цитата:
![]() Это так было сказано к слову и не значит, что я горю расжигать перебранку. ![]() Цитата:
Цитата:
Цитата:
![]() 1) (уже писалось почему) Решено было использовать стандартную библиотеку С и WinAPi для работы с файлами и каждый раз конвертить string в char* надоело бы, да и неизвестно мне насколько это эффективно 2) Нельзя использовать string::c_str() для изменения, что доставляло бы хлопот при работе с нек. функциями, например с fwrite. 3) В этом случае возможны ненужные выделения памяти |
|||||||
![]() |
![]() |
# 15 | |||
Member
Регистрация: 10.03.2002
Адрес: Israel
Сообщения: 245
![]() ![]() |
Цитата:
Я же привел вам нормальное ( относительно конечно , я бы все равно так бы не писал) решение в третьем посте, с нормальным конструктором и с нормальным использованием вектора. Чем оно не понравилось ? Ваше решение изначально кривое, вы не правильно работаете с вектором и последствия этой неправильной работы пытаетесь заткнуть с помощью всяких ухищрений при этом обвиняя STL в плохой реализации. Еще раз настоятельно советую почитать документацию по STL. Возможно даже сможете вместо fwrite использовать стримы STL. ![]() Цитата:
Посчему бы не использовать STL для работы с файлам вместо WinAPi? Цитата:
__________________
Best Regards |
|||
![]() |
![]() |
# 16 | |
Junior Member
Регистрация: 19.04.2002
Адрес: Дом
Пол: Male
Сообщения: 187
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() |
Цитата:
Ну да ладно.
__________________
Дураки не динозавры - они не вымрут... |
|
![]() |
![]() |
# 18 | ||||
Junior Member
Регистрация: 13.05.2004
Сообщения: 128
![]() ![]() |
to alexey_ma
Такое ощущение, что мы говорим на разных языках. Цитата:
Цитата:
Что насчёт вашего решения, то она мне не понравилось из-за ТРЁХ НЕ нужных выделений и освобождений памяти. К тому же ваше решение "педалится", если строка содержит символ '\0' (конечно, со string это не проблема и Ваш вариант можно немного модифицировать, но это так к слову) Цитата:
![]() Цитата:
[hr] И вообще alexey_ma я считаю, что не нужно развивать эту тему ![]() Я знаю, что мой код "кривой". Но также знаю, что этот код писался для меня и врядли будет сопровождаться и маштабироваться ![]() Более того, я понял, что малой кровью от "кривизны" (в моём понимании) не избавиться. Поэтому, возможно, в будущем всё перепишется, когда больше прокачаюсь. А сейчас я не вижу лезть в код, кот., вроде, прекрасно пока работает. |
||||
![]() |