| 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 в 23: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 в 10: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 в 23:06. |
|
|
|
|
# 4 | |||||
|
Junior Member
Регистрация: 13.05.2004
Сообщения: 128
![]() ![]() |
Цитата:
. Начал недавно переходить с С на С++. С бинарными файлами на С++ я не работал ещё, поэтому решил изпользовать стандартную библиотеку С (чтобы не тратить лишнее время на поиск множества багов, кот. всегда выскакивают, когда начинаешь что-то изучать).Цитата:
В проге структура используется только для инициализации и чтобы запихнуться в вектор. Цитата:
. В первом посте это и написано. Проблема, только что нигде перед использованием НЕ удаляются элементы из вектора и как было также написано деструкторы элементов НЕ вызывались.Цитата:
Цитата:
З.Ы. Как я понял, изящней последнего решения (то, кот. с наследованием) в моём первом после ничего не придумано. Его я и реализовал. Последний раз редактировалось Crazy_kettle; 30.01.2006 в 00:03. |
|||||
|
|
|
|
# 5 | |||
|
Full Member
Регистрация: 16.10.2002
Адрес: ArchLinux, Internet
Сообщения: 557
![]() ![]() ![]() ![]() |
Цитата:
. Кстати, в gcc не копируется :DЦитата:
Цитата:
Бо вторых, по моему правильное решение написать их все таки так как твоэ решени может упасть на каком-то другом сценарии - все так там возможна двойная деаллокация. |
|||
|
|
|
|
# 6 | ||
|
Member
Регистрация: 10.03.2002
Адрес: Israel
Сообщения: 245
![]() ![]() |
Цитата:
Цитата:
__________________
Best Regards |
||
|
|
|
|
# 7 | |
|
Junior Member
Регистрация: 19.04.2002
Адрес: Дом
Пол: Male
Сообщения: 187
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() |
Цитата:
Уважаемый, MS тут не причем. STL он и в Африке STL. И в доках четко и ясно написано что конструкторы копирования и операция присваивания для пользовательских классов при хранении их не по указателю необходимы.
__________________
Дураки не динозавры - они не вымрут... Последний раз редактировалось PSyton; 30.01.2006 в 14:32. |
|
|
|
|
|
# 8 | ||||||||
|
Junior Member
Регистрация: 13.05.2004
Сообщения: 128
![]() ![]() |
Цитата:
. Кстати, Имхо вызов конструктора не вредит. Другое дело деструктор, но его я убрал . Память освобождается при очистке вектора. Да это решение не идеально, т.к. нужно иметь ввиду возможные кросс-референсы и memory-leaks (при использовании комманд копирования и изменения вектора, отличной от clear). Но это на данный момент не используется! Более того, написав copy constructor и operator=, я потеряю в скорости, т.к. будет происходить как минимум 2 НЕ ОБЯЗАТЕЛЬНЫХ копирований строки (от одного можно избавиться, но не от второго ).[hr] Цитата:
Цитата:
Цитата:
Цитата:
.Не предусмотрел я то, что при реализации vector: ush_back может создаваться временный объект (что вы категорически отрицаете) и для него вызываться деструктор, который всё портит. Я не виню за это программистов Microsoft, это их дело как писать свои процедуры.Цитата:
[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; 31.01.2006 в 00: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 в 12:54. |
|
|
|
|
|
# 14 | |||||||
|
Junior Member
Регистрация: 13.05.2004
Сообщения: 128
![]() ![]() |
alexey_ma вы наверное очень умный, но, имхо, это не значит, что это даёт Вам право [внимательно\вовсе] НЕ читать посты других участников.
Цитата:
Цитата:
Цитата:
).Это так было сказано к слову и не значит, что я горю расжигать перебранку. ![]() Цитата:
Цитата:
Цитата:
(было выловлено место ошибки и написан специально под неё пример). Конечно, в программе можно было заменить все char* на string, но это решено было не делать по следующим причинам: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 я считаю, что не нужно развивать эту тему .Я знаю, что мой код "кривой". Но также знаю, что этот код писался для меня и врядли будет сопровождаться и маштабироваться .Более того, я понял, что малой кровью от "кривизны" (в моём понимании) не избавиться. Поэтому, возможно, в будущем всё перепишется, когда больше прокачаюсь. А сейчас я не вижу лезть в код, кот., вроде, прекрасно пока работает. |
||||
|
|