QNX RTP Logo QNX Realtime Platform: Русский Портал QNX
Thursday, 20 Nov 2008 15:47
Меню

Проект OpenNET - все о Unix
Главная

 · Начало · Статистика · Поиск ·

  QNX.ORG.RU —› Языки и алгоритмы —› STL вопросы по шаблонам <list> и <vector>

Посл.ответ Сообщение


Дата: 26 Янв,  14:33 · Поправил: nickola

Здравствуйте,
есть два вопроса по <list> и <vector>:
1. Если создавать список, состоящий из динамических элементов, то как обеспечить корректное освобождение памяти и надо ли это делать вообще в этом случае? Пример внизу:

#include <cstdlib>
#include <iostream>
#include <list>

class test {
public:
int *x;
test(int y){printf("constructor " ); x=new int [y]; *x=y;}
~test(){printf("destructor " ); delete [] x;}
};

list <test> test_list;
list <test>::iterator p;

int main(int argc, char *argv[]) {

test_list.push_back(test(1));
p=test_list.begin();
printf("x=%d ",*(p->x));

return EXIT_SUCCESS;
}

# ./test
constructor
destructor
x=1
destructor
Memory fault (core dumped)
#

2. Если создавать список на основе шаблона <vector>, как получить указатель на отдельный элемент вектора?
SETIOV(reply_iov,vector[i],size) не получилось, ошибка приведения типов.


Дата: 26 Янв,  14:55

вопервых - Где конструктор копирования????!!!!!!!!
Если есть динамические переменые внутри класа - один из первых вопросов должен возникать.. что и как делать в конструкторе копирования.

Тобишь.. если где-либо вызвать некую функцию.. которая будет принимать твой объект по значению - у тебя несколько раз(приблизительно ) вызовуца конструкторы копирования для временных обхектов(которые в твоем коде - поумолчанию копировать будут..тока значение указателя).. и несколько раз (так же приблизительно ).. вызовется деструкторы для этих временных объектов (которые уже удаляют сам объект)...
Если об этом не позаботица - результат - в некоторых случаях - утечка ресурсов (помогает в этом конструктор копий.. который компилятор сгенирировал поумолчанию)... и в некоторых случаях - вываливание программы (об этом позаботился диструктор - он удалил один и тот же объект несколько раз.. вернее попытался это сделать )


Дата: 26 Янв,  15:01 · Поправил: Olej

nickola
Если создавать список на основе шаблона <vector>, как получить указатель на отдельный элемент вектора?



struct marshrut {
...
};
typedef map<int, marshrut, less<int> > idmarsh_t;
static idmarsh_t marsmap;
...
int ownid = ...
const marshrut *m = &( marsmap[ ownid ] );

- это из живого проекта, который сегодня у меня в работе: там map, но он ничем не хуже vector.


Дата: 26 Янв,  15:04

Что касаеца второго вопроса. Если тебе нужно получить указательна объект хранимый в векторе.. можешь попользоваца соотвествующи механизмом

std::vector <SSomeStruct> Vector;
//.......
SSomeStruct *pTempObject;
//....
pTempObject = &Vector[n];

....
Правила теже.. pTempObject - теперь являеца указателем на объект хранимый в Vector на соответвующей n-ой позиции (разумееца с нуля считаем ). Тока смотри, бди.. чтобы n лежало в нужном диапазоне тоесть <Vector.size().. да и собственно чтобы вектор был не пустым.. хотя можно и ограничица тока векторсайзом ) )

Можно дальше пойти - *(pTempObject+k) .. поидее эквивалентно &Vector[n+k].. ну при условии что выше упомянутые ограничения также соблюдаюца и с (n+k)


Дата: 26 Янв,  15:15

nickola
1. Если создавать список, состоящий из динамических элементов, то как обеспечить корректное освобождение памяти и надо ли это делать вообще в этом случае?


Дела ваши хуже, чем вы предполагаете :
- все операции над элементами контейнеров (вставка, перенос и т.д.) делаются исключительно копированием (отсюде: обязательно должен быть ваш конструктор копирования, о чём уже сказали);
- это значит, что у вас будет ещё дополнительные операции копирования там где вы и не предполагаете - и с операциями удаление временных объектов...
P.S. из той же оперы, например:
void func( test ) {...}
test func( void ) {...}
а вот с:
void func( test& ) {...}
таких проблем не будет.

Как часто удаётся делу помочь? : храните контейнер указателей на свои созданные динамические объекты.


Дата: 26 Янв,  15:35

Olej
...храните контейнер указателей на свои созданные динамические объекты....

если бы все так было просто )) - с этим свои яйца...
Об объектах в этом списке как-то надо будит позабодица (об удалении к примеру ... кто за это будет отвечать).. тоесть, что хотелось яко бы на первый взгляд.. чтобы делалось автоматом... и думалось что об этом позаботица контейнер+сам объект - оказывается... необходимо еще "нечто".. что будит "подгребать" за всем...

Капнем дальше.. используй совет Olej, но почитай и поискай "умные указатели".. и в том же направлении )


Дата: 27 Янв,  11:17 · Поправил: Olej

lastcross
но почитай и поискай "умные указатели".. и в том же направлении )


Это уже ... дело вкуса :
- динамические объекты, которые "тянет" поместить в контейнер, это так практически всегда - создаются намного (в десятки-сотни раз) реже, чем производятся с ними операции: перемещения, копирования, временные объекты etc.
- поэтому не так страшно массово "наследив" потом корректно за собой всё уничтожить ; )
- из "умных указателей" можно, конечно, использовать и ... "не зело умные", например auto_ptr из того же STL (или, точнее, "из около STL" ) ... или более развитые ... "a'la Александреску" (библиотеки boost etc.)...
- но это тоже "палка о 2-х концах": всякая система, осонованная на подсчёте ссылок хороша до тех пор, пока не появится где-то циклическая ссылка - это разработчики на Perl лучше всех знают , а потом: memory leak и крах embedded системы ...
- так что, как по мне, в системах такого класса лучше ... "не не сорить, а убирать за собой"(с)


Дата: 28 Янв,  00:54

Olej
..Это уже ... дело вкуса ...

Я бы даже сказал - зависит от поставленной конкретной задачи. Согласитесь, vector<char> имеет куда больше приимуществ по сравнению с vector<char*> (как минимум гемороя меньше..да чуть и код побезопаснее будит).В общем случае - контейнер указателей(динамически созданых объектов) имеет больше приимуществ(тоже быстродействие..,размер контейнра,количества "ненужных" операций.. при копировании ...,вставки и тд)..но только при одном условии что человек помнит что он хранит в контейнере указатели..а не объекты..И что эти объекты он создал динамически "сам" .. поэтому их нужно когданить будет обязательно удалить. Руками делать это несколько по мазохиски.. легко нарватся можно на утечку памяти.

Что касается auto_ptr - пользоватся им не советую.. а точнее им не советует пользоваца Скотт Майерс)). Описание проблемы - в "Эфективное использование STL".. в совете8))). В кратце - очень неприятные моменты возникают когда к/с контейнером таких вот автопэтээрными объектами.. применяется некая функция... с переприсваиванием(инициализацией)..некой временной внутренней переменной этой функции одним из элементов этого контейнера...Тогда иметь будим - как утечку памяти..так воще непредсказуемую работу функции(алгоритма).

Насчет циклических ссылок - )).. а если я скажу что многие(если даже не все) механизмы синхронизации - могут привести к клинчу процессов.. то тогда.. что?..не пользоваца... ими чтоли? ))... Проблема не в механизме.. проблема в дизайнере )))).

Убирать за собой? ... как же так?.. Я так понял..вы не придерживаетесь этой точки зрения - ибо идея - что объект сам должен побеспокоица об удалении самого себя..хм..вернее подчищать за собой, что собственно и делают умные указатели (shared_ptr того же буста).. - вам не подуше ).

Кстати заметим между прочим ).. что с использованием умных указателей - в конечном счете храняца в контэйнере не указатели..а объекты - "умные указатели"[img]http://qnx.org.ru/components/minibb/img/smilies/wink.gif[/img].

А вообще.. советовать человеку, который ненаписал (или забыл написать) конструктор копирования..когда он явно нужен, использвоать в качестве элементов контэйнера указатели - преступлению подобно. Он их таким же образом забудит удалить..или просто потеряет - тогда совсем прячься..лазять по битым указателям приниприятнейшее зрелище.


Дата: 28 Янв,  23:37

- Offtopic -
2 lastcross
Не сочтите за снобизм, но хочу Вас попросить писать хоть чуть-чуть аккуратнее - уж больно тоскливо в одно и то же время фильтровать небрежность письма и понимать смысл написанного.
Неужели Вы и на С++ пишите так же небрежно? Тогда не хотел бы я столкнуться с вашими произведениями - никакие "умные указатели" положения не спасут...


Дата: 29 Янв,  08:07

Ужас блин!)
О как…
Согласен, в следующий раз постараюсь быть намного лаконичнее и понятливее для окружающих, дабы люди коих возмущает стиль письма моего, а также присутствие в них ошибок, и коим мешает «фильтровать небрежность» не утруждались поправлять меня.

Однако прошу понять (и апеллирую к высшим чувствам программизма Вашего), «небрежность» эта вызвана стремлением вопрошающего использовать механизм ему известный ..хм.. мягко сказать… НААБУМ … лишь только имея общие понятия что ОНО такое, а также что и как ОНО делает...при этом не имея никакого желания в дальнейшем расширять эти знания (в частности – отсутствие конструктор копирования при использовании динамических переменных внутри класса… или совет - использовать auto_ptr….). Честно говоря – сам такой .. отчасти . Но обиды на людей которые отсылают меня в таких случаях к «умным» книгам – не держу. Суть всех ответов-то был – "Вы неверно используете стандартные контейнеры, прочитайте пару книжек".
Да и, по сути - есть за мной грешок – пишу я с ошибками )). Но… пусть тот, кто ошибок не допускает – пусть он кинет в меня камнем.))

Еще раз прошу прощения. Не знал куда положить подобное. По сему - ложу сюда .

Для Evgeniy..
Что Вы!.. Сие замечание снобизмом не считаю. Снобизмом больше будет замечание по поводу стиля программирования. А здесь он ужасен был )))).
Насчет произведений…Вряд ли столкнетесь…я бы даже сказал – к моему сожалению …К критике отношусь весьма положительно.


Дата: 29 Янв,  08:40 · Поправил: nickola

Практики на c++ пока маловато, совсем забыл что конструктор копирования вообще существует.
Т.е. для первого конкретного примера достаточно добавить конструктор копирования и все будет корректно?
(здесь первый элемент *x содержит размер выделяемой памяти)
test(const test &tst){
x=new int[*(tst.x)];
}


Дата: 29 Янв,  09:51

nickola
Т.е. для первого конкретного примера достаточно добавить конструктор копирования и все будет корректно?
(здесь первый элемент *x содержит размер выделяемой памяти)
test(const test &tst){
x=new int[*(tst.x)];
}


1. "корректно" - это ещё вовсе не эквивалентно "хорошо"

2. .... ? "... уже намазывается, но запах ещё очень сильный сохраняется..."(с) : копирование - это и есть "копирование", для этого ещё недостаточно просто отвести кусок "мусорной" памяти...
P.S. кроме того, я, к примеру, совсем не понял смысла замечания: "(здесь первый элемент *x содержит размер выделяемой памяти)" - с какой это стати он содержит? да и по фрагменту кода - "содержит" он указатель на эту память, а не её размер.

3. хотя бы как-то так в самом простом виде:

class test {
public:
int *x, s;
test( int y ) {
x = new int [ s = y ];
};
test( const test& t ) {
memcpy( x = new int[ s = t.s ], t.x, t.s );
};
~test( void ) { delete [] x; };
};


Дата: 29 Янв,  11:02

"кроме того, я, к примеру, совсем не понял смысла замечания: "(здесь первый элемент *x содержит размер выделяемой памяти)"

в моем примере было x=new int [y]; *x=y;

"для этого ещё недостаточно просто отвести кусок "мусорной" памяти..."

А в данном случае, именно при использовании списка из динамических элементов, зачем инициализировать копию массива, если после завершения функции push_back сразу вызывается деструктор?


Дата: 29 Янв,  11:18 · Поправил: lastcross

..мда..опоздал :)

#include <list>
#include <algorithm>
#include <iostream>
#include <iterator>

using namespace std;

class KTest{
public:
KTest(int *Data,unsigned int Size):_x(New(Data,Size)),_size(Size){
}//KTest

KTest(const KTest &Obj):_x(New(Obj._x,Obj._size)),_size(Obj._size){

};


KTest & operator =(const KTest &Obj){
if(&Obj!=this){
if(_size<Obj._size){
int *pTemp = New(Obj._x,Obj._size);
_x = pTemp;
_size = Obj._size;
}//if
else{
memcpy(_x,Obj._x,Obj._size);
}//else
}//if

return (*this);
};//operator =(const KTest &Obj)

//интерфейсы - иже с ними (простейшие.. а лучше бы итераторы)
inline int* const getX()const {return _x;};
inline unsigned int getSize()const{return _size;}

virtual ~KTest()throw(){delete [] _x;};
protected:
int * New (int *Data,unsigned int Size){
int * pNew = new int [Size];
memcpy(pNew,Data,Size*sizeof(int));
return pNew;
}//New (int *Data,unsigned int Size)

private:
int * _x;
unsigned int _size;
};//KTest

struct SListPrint{
public:
void operator()(const KTest &Obj){
const int *BufferX = Obj.getX();
copy(BufferX,BufferX+Obj.getSize(),std::ostream_iterator<int>(std::cout," "));
std::cout<<endl;
}//operator
};//SListPrint

int main(int argc, char* argv[])
{
int A[]={1,2,3,4,5};
int B[]={5,4,3,2,1};
KTest ObjectTest1(A, (sizeof(A)/sizeof(int)) );
KTest ObjectTest2(B, (sizeof(B)/sizeof(int)) );
list <KTest> List;
List.assign(12,ObjectTest1);
List.push_back(ObjectTest2);
List.push_back(KTest(A, (sizeof(A)/sizeof(int))-2 ));
for_each(List.begin(),List.end(),SListPrint());
}
//---------------


Дата: 29 Янв,  12:00 · Поправил: lastcross

Тут главное, суметь проконтролировать вызовы деструкторов и конструкторов – чтобы у тебя не получилось: конструктор копий создал объект, который явно будет ссылаться на (динамические) данные другого экземпляра класса (толи ссылками, толи указателями), а деструкторы же этих объектов бесконтрольно удаляли эти данные (притом один удалит корректно, а второй будет удалять мусор).
В результате, что было у тебя –

test_list.push_back(test(1));

-создал временный объект (с выделением памяти)

-раньше у тебя конструктор копий был создан по умолчанию компилятором , так как ты его не объявлял самостоятельно (то есть – он делал простое присваивание указателей одного объекта другому… тем самым и создаваемый объект и объект которым мы инициализировали –ссылались на одну и туже выделенную память )

-этот временный объект передавался push_back – который в своем теле создавал новый объект (который собственно и сохраняется в списке) путем вызова конструктора копий твоего класса (вот они ..два объекта ссылаются на одну область памяти)

- дальше выход из push_back – удаляется временный объект – вызывается его деструктор, удаляется динамически созданный объект внутри класса. Но ведь тот объект который внутри списка.. он ведь ссылается на эту.. уже удаленную область памяти. Яиц пока не каких не происходит .

- они вываливаются… как только необходимо уничтожить тот объект, который продолжает лежать в списке. У тебя это происходит во время удаления самого списка – то есть когда он покидает свою область жизни – то есть функцию main().


Конструкция:
x=new int[*(tst.x)];

- вообще .. зачем вам это? Нужно ведь выделить памяти столько сколько занимает инициализирующий объект (зачем здесь лепить все в одну кучу – данные объекта и его свойства.. т.е. размер?) Во вторых, само выделение памяти вы может и сделали (если допустить такую мысль) – но эту память не проинициализировали данными которые хранятся в объекте инициализации. Таким образом содержимое объекта this->x сравнивая с tst.x – абсолютно не идентичны (если отметать разного рода вероятности). И после new у Вас в x – лежит мусор. И тогда вопрос- разве это будет копией объекта на которыый ссылается tst?


Дата: 29 Янв,  12:06

И вообще, используй вместо своего test::x вектор

vector<int> x;

избавишься от порядочного гемороя


Дата: 29 Янв,  12:20 · Поправил: nickola

"здесь лепить все в одну кучу – данные объекта и его свойства.. т.е. размер?"
да это просто приведено в качестве примера, на самом деле у меня отведено отдельное поле под размер и сам класс несколько сложнее.

#include <cstdlib>
#include <iostream>
#include <list>

using namespace std;

class test {
public:
int *x;
test(int y){printf("constructor %d",y); x=new int [y]; *x=y;}
test(const test &o){printf("copy constructor %d",*(o.x)); x=new int [*(o.x)] ;}
~test(){printf( "destructor" ) ; delete [] x;}
};

list <test> test_list;
list <test>::iterator p;

int main(int argc, char *argv[]) {
int i;

for(i=1;i<4;i++){
test_list.push_back(test(i));
}
p=test_list.begin();
while(p!=test_list.end()){
printf("x=%d
",*(p->x));
p++;
}

return EXIT_SUCCESS;
}


# ./test
constructor 1
copy constructor 1
destructor
constructor 2
copy constructor 2
destructor
constructor 3
copy constructor 3
destructor
x=1
x=2
x=3
destructor
destructor
destructor
#

И зачем мне инициализировать копию, если я сам ее никак не использую? Единственный случай, когда у меня объект передается в функцию, это вызов push_back.


Дата: 29 Янв,  12:27

Ух...чертяка )))
ты сам понял что ты вывел ))?

test(const test &o){printf("copy constructor %d",*(o.x)); x=new int [*(o.x)] ;

ты попробуй
test(const test &o ) { x=new int [*(o.x)]; printf("copy constructor %d",* (x) ) ; }

Заметишь разницу


Дата: 29 Янв,  12:46

Конечно замечу разницу. Естественно она есть. Но зачем мне пробовать printf("copy constructor %d",* (x) ) ;?
Мне копия не нужна. В данном случае она создается, когда вызывается функция push_back, что там внутри происходит я не знаю, а после возвращения из этой функции вызывается деструктор.
Вопрос-то был такой, нужно ли в данном конкретном случае, в моем примере,если используется только функция push_back, инициализировать копию?


Дата: 29 Янв,  13:10

Ну какой ты непонятливый -то )). Надо посмотреть .. есть тут рядом Evgeniy аль нету, а то ей богу - терпения уже нету))))
Вы мне необъясните случайно, почему Ваш код (абсолютно скопированный с отсюдова - один в один) у меня выдал

x=844586344
x=844586344
x=844586344

...?

Спрашиваешь зачем? )
Дак потому как везде, где будет производиться копирование объекта (посредством твоего конструктора копий) - везде будет копия не сответвовать оригиналу.. то есть таковой не будет являться. Потому и у меня x - хрен какой.
Тебе не нужен - ДАК ОН НУЖЕН ТВОЕМУ СПИСКУ..ЧТОБЫ ОН ХРАНИЛ В СЕБЕ ..ИДЕНТИЧНУЮ!!!!!!!!!...КОПИЮ ТВОИХ ОБЪЕКТОВ которые ты туда пихаешь )


Дата: 29 Янв,  13:10

nickola
Вопрос-то был такой, нужно ли в данном конкретном случае, в моем примере,если используется только функция push_back, инициализировать копию?

Проще это запомнить как правило, как аксиому - навсегда: нужно


Дата: 29 Янв,  14:05

"Вы мне необъясните случайно, почему Ваш код (абсолютно скопированный с отсюдова - один в один) у меня выдал"

Да потому что я код неправильный сюда написал, писал по памяти, а из qnx скопировал только вывод в терминал
Конструктор копий на самом деле был такой
test(const test &o){printf("copy constructor %d ",*(o.x)); x=new int [*(o.x)]; *x=*(o.x);} вот все и работало
Сам себя запутал, теперь все понятно.
Спасибо за ответы


Дата: 29 Янв,  15:37

Внимательно посмотри на то что ты делаешь.
Ты только производишь инициализацию первого элемента x - а остальные опять же мусором инициализируется ) - у тебя же там ЦЕЛЫЙ МАСИВ!


Дата: 30 Янв,  07:25

Да понял я, что надо инициализировать копию и всю целиком серьезно
А пост выше объяснял почему "Ваш код (абсолютно скопированный с отсюдова - один в один) у меня выдал..."
Все равно еще раз спасибо

You must login to post.

©   2000-2003 Команда проекта QNX.ORG.RU // QNX.ORG.RU Team
Авторы проекта: Дмитрий Алексеев [dmi] и Дмитрий Васильев. Техническое сопровождение проекта: Игорь Сорокин [isorokin]. Информационное сопровождение: Дмитрий Алексеев [dmi]
QNX - зарегистрированная торговая марка QNX Software Systems, Ltd., Canada. Остальные упоминаемые на сайте торговые марки и логотипы являются исключительно собственностью их уважаемых владельцев. Ничьи права не затронуты. Материалы сайта не могут быть скопированы и где-либо использованы в той или иной форме без письменного разрешения разработчиков сайта.
Powered by Mambo Open Source