Re: std::vector. как правильно упаковать в вектор объект
От: pavel_turbin  
Дата: 04.09.06 16:33
Оценка: +1 :)
если хочется работать с указателями и stl-ем, то
лучше пихать в vector SmartPtr, примеры можно найти в boost. Тогда только об удалении можно не думать. Хотя в кривых руках и калькулятор зависнет.
std::vector. как правильно упаковать в вектор объект
От: Аноним  
Дата: 04.09.06 14:27
Оценка:
Доброго времени суток,

столкнулся с интересной проблемой при использовании std::vector. Суть проблемы (сперва код):
class B
{
   public:
     BOOL ExecuteThread();
   .
   .
   .
   protected:
     virtual DWORD vThreadProc(); 
};

класс просто инициализирует поток, а затем в нужный момент его запускает. Далее
class A
{
   public:
     A();
     ~A();
     BOOL Init();
     BOOL Start();
   .
   .
   .
   private:
     vector<B> vec;
};

A::A()
{
  .
  vec.reserve(10);
  .
}

A::~A()
{
   vector<B>::iterator iter = vec.begin();
   while(iter != vec.end())
   {
      delete iter;
      iter++;
   } 
}

BOOL A::Init(параметры)
{
  B *pB;
  pB = new B(параметры);
  .
  vec.push_back(*pB);
  .
  delete pB;
  .
}

BOOL A::Start()
{
  vector<B>::iterator iter = vec.begin();
  while(iter != vec.end())
  {
     iter->ExecuteThread();
     iter++;
  }
}

А теперь суть проблемы. Предположит во время выполнения инициализируются 3 объекта B с различными параметрами, а соответсвенно и 3 различных потока.
Метод A::Init() будет вызван 3 раза, а все объекты упакуются в вектор. Вот тут и проблема: в вектор правильно пишется только первый объект, остадбные записываются (увеличивается размер элементов вектора, но внутри ячейки лежит мусор).

Курез ситуации в том, что исходя из вышепнаписанного должен заработаль только первый поток, посредством B::ExecuteThread(), но они запускаются все, тесть метод A::Start() отрабатывает без ошибок, а итератор возвращает данные правильно.

Зато в деструкторе первый объет уничтожается нормально,а на втором всё падает с Access Violation, тоесть опять в векторе виден только один элемент (правильно виден). Размер вектора при вызове деструктора тоже 3.

В чём моя ошибка, может уже кто сталкивался? Операторы присваивания и конструкторы копирования класса B написаны и отлично работают.
Может нужны какие-то дополниетельные инициализации самого вектора???

Заранее благодарен всем откликнувшимся.

Добавлено форматирование — Кодт
Re: std::vector. как правильно упаковать в вектор объект
От: Аноним  
Дата: 04.09.06 14:56
Оценка:
Здравствуйте, Аноним, Вы писали:


А>class A
А>{
А>   public:
А>     A();
А>     ~A();
А>     BOOL Init();
А>     BOOL Start();
А>   .
А>   .
А>   .
А>   private:
А>     vector<B> vec;
А>};

[ кусь ]

А>A::~A()
А>{
А>   vector<B>::iterator iter = vec.begin(); // Этот код не может не падать. ВЕКТОР УДАЛИТ ЭЛЕМЕНТЫ САМ.
А>   while(iter != vec.end())
А>   {
А>      delete iter;
А>      iter++;
А>   } 
А>}

А>BOOL A::Init(параметры)
А>{
А>  B *pB;
А>  pB = new B(параметры); // Зачем создавать по new? на стеке тоже пректасно можно создавать элементы.
А>  vec.push_back(*pB);    // Я бы написал vec.push_back( B(параметры) );
А>  delete pB;
А>}

[кусь]

А>Операторы присваивания и конструкторы копирования класса B написаны и отлично работают.


Код
B( const B & )
const B operator = ( const B & )
~B();
В студию.
Re[2]: std::vector. как правильно упаковать в вектор объект
От: __bobik__ Украина http://farnetstat.narod.ru/
Дата: 04.09.06 20:50
Оценка:
Здравствуйте, pavel_turbin, Вы писали:

_>если хочется работать с указателями и stl-ем, то

_>лучше пихать в vector SmartPtr, примеры можно найти в boost. Тогда только об удалении можно не думать. Хотя в кривых руках и калькулятор зависнет.

А если охота возиться с сырыми указателями то вектор нужно инстанцировать указателями на B, типа такого ...
std::vector<B *> SomeObject;
Re[2]: std::vector. как правильно упаковать в вектор объект
От: __GnoM  
Дата: 04.09.06 21:40
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, Аноним, Вы писали:



А>
А>>class A
А>>{
А>>   public:
А>>     A();
А>>     ~A();
А>>     BOOL Init();
А>>     BOOL Start();
А>>   .
А>>   .
А>>   .
А>>   private:
А>>     vector<B> vec;
А>>};

А>[ кусь ]

А>>A::~A()
А>>{
А>А>   vector<B>::iterator iter = vec.begin(); // Этот код не может не падать. ВЕКТОР УДАЛИТ ЭЛЕМЕНТЫ САМ.
А>>   while(iter != vec.end())
А>>   {
А>>      delete iter;
А>>      iter++;
А>>   } 
А>>}

Можно уточнить, вектор удалит элементы сам путём вызова деструктора ~B() или как???
Просто если в B имеет место динамическое выделение памяти, необходимо гарантировать её стопоцентное освобождение???

Да и вопрос остался. Падает только на втором элементе вектора, первый успешно удаляется!

А>>BOOL A::Init(параметры)
А>>{
А>>  B *pB;
А>>  pB = new B(параметры); // Зачем создавать по new? на стеке тоже пректасно можно создавать элементы.
А>>  vec.push_back(*pB);    // Я бы написал vec.push_back( B(параметры) );
А>>  delete pB;
А>>}

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

А>

А>[кусь]

А>>Операторы присваивания и конструкторы копирования класса B написаны и отлично работают.


А>Код

А>B( const B & )
А>const B operator = ( const B & )
А>~B();
А>В студию.

С удовольствием бы выкинул, но пример приводимый тут снят с разрабатываемой модели, а поэтому если выкидывать реализацию операторов, то придётся выкладывать и много сопутсвующего материала

да и ещё
А>const B operator = ( const B & )

не слишком рекомендуется, лучше

B& operator= (const B& rhs), причины хорошо описаны Скотом Мейерсом (см. правило 15).
Re[3]: std::vector. как правильно упаковать в вектор объект
От: Alex_Avr Россия  
Дата: 05.09.06 08:23
Оценка:
Здравствуйте, __GnoM, Вы писали:

А>>[ccode]

А>>>class A
А>>>{
А>>> public:
А>>> A();
А>>> ~A();
А>>> BOOL Init();
А>>> BOOL Start();
А>>> .
А>>> .
А>>> .
А>>> private:
А>>> vector<B> vec; Элементы вектора хранятся по значению
А>>>};

А>>>A::~A()

А>>>{
А>>А> vector<B>::iterator iter = vec.begin(); // Этот код не может не падать. ВЕКТОР УДАЛИТ ЭЛЕМЕНТЫ САМ.
А>>> while(iter != vec.end())
А>>> {
А>>> delete iter;
А>>> iter++;
А>>> }

А>>>}

__G>Можно уточнить, вектор удалит элементы сам путём вызова деструктора ~B() или как???

__G>Просто если в B имеет место динамическое выделение памяти, необходимо гарантировать её стопоцентное освобождение???

__G>Да и вопрос остался. Падает только на втором элементе вектора, первый успешно удаляется!


В vec элементы хранятся по значению, т.е. вектор сам их создает и уничтожает.
Поэтому руками удалять их _нельзя_, и этот while _нужно_ убрать. Иначе ошибка
будет возникать в деструкторе класса vector.

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

В данном случае интератор, указывающий на начальный элемент вектора — это просто указатель
на блок памяти, выделенный классом vector. Поэтому первое удаление происходит нормально (да и то,
у других компиляторов здесь могла возникнуть ошибка из-за выделения по new[], а удалению по delete).
На следующей итерации итератор-указатель указывает на следующий элемент вектора, т.е. адрес
который находился внутри уже удаленного блока памяти.

Таким образом получается, что производится попытка освобождения памяти, начиная с адреса, с которого
не выделялся ни один из блоков памяти, не говоря уже о том, что удаление блока памяти,
куда входил этот элемент вектора, уже произошло.

В результате — повреждение кучи с перспективой искать причины странных и непостоянно проявляющихся ошибок.
Можно сказать, что в данном случае вам еще повезло, что ошибка сразу возникает.

Так что либо в приведенном коде опечатки, либо автор кода не представляет,
что из себя представляет шаблонный класс вектора и как его можно использовать.

__G>Данный метод — это просто пример для обсуждения, в реально используемом классе нужно было гарантировать правильное создание объекта и инициализацию его параметров, поэтому создаётся динамически. Неудобно, но другого варинта не нашлось, пока....


Тогда лучше описать, какую задачу решаете по созданию объекта, может кто и лучше чего посоветует.
С уважением, Александр Авраменко.
Re[4]: std::vector. как правильно упаковать в вектор объект
От: __GnoM  
Дата: 05.09.06 10:59
Оценка:
Здравствуйте, Alex_Avr, Вы писали:

A_A>Здравствуйте, __GnoM, Вы писали:


А>>>[ccode]

А>>>>class A
А>>>>{
А>>>> public:
А>>>> A();
А>>>> ~A();
А>>>> BOOL Init();
А>>>> BOOL Start();
А>>>> .
А>>>> .
А>>>> .
А>>>> private:
А>>>> vector<B> vec; Элементы вектора хранятся по значению
А>>>>};

А>>>>A::~A()

А>>>>{
А>>>А> vector<B>::iterator iter = vec.begin(); // Этот код не может не падать. ВЕКТОР УДАЛИТ ЭЛЕМЕНТЫ САМ.
А>>>> while(iter != vec.end())
А>>>> {
А>>>> delete iter;
А>>>> iter++;
А>>>> }

А>>>>}

__G>>Можно уточнить, вектор удалит элементы сам путём вызова деструктора ~B() или как???

__G>>Просто если в B имеет место динамическое выделение памяти, необходимо гарантировать её стопоцентное освобождение???

__G>>Да и вопрос остался. Падает только на втором элементе вектора, первый успешно удаляется!


A_A>В vec элементы хранятся по значению, т.е. вектор сам их создает и уничтожает.

A_A>Поэтому руками удалять их _нельзя_, и этот while _нужно_ убрать. Иначе ошибка
A_A>будет возникать в деструкторе класса vector.

A_A>В вышепреведенном коде происходит следующее.

A_A>В векторе данные хранятся непрерывно одним блоком памяти. В используемой вами STL итератор
A_A>определен как указатель (в STLPort в отладочном режиме это не так, поэтому этот код c STLPort даже бы не скомпилировался).

A_A>В данном случае интератор, указывающий на начальный элемент вектора — это просто указатель

A_A>на блок памяти, выделенный классом vector. Поэтому первое удаление происходит нормально (да и то,
A_A>у других компиляторов здесь могла возникнуть ошибка из-за выделения по new[], а удалению по delete).
A_A>На следующей итерации итератор-указатель указывает на следующий элемент вектора, т.е. адрес
A_A>который находился внутри уже удаленного блока памяти.

A_A>Таким образом получается, что производится попытка освобождения памяти, начиная с адреса, с которого

A_A>не выделялся ни один из блоков памяти, не говоря уже о том, что удаление блока памяти,
A_A>куда входил этот элемент вектора, уже произошло.

A_A>В результате — повреждение кучи с перспективой искать причины странных и непостоянно проявляющихся ошибок.

A_A>Можно сказать, что в данном случае вам еще повезло, что ошибка сразу возникает.

A_A>Так что либо в приведенном коде опечатки, либо автор кода не представляет,

A_A>что из себя представляет шаблонный класс вектора и как его можно использовать.

Спасибо, объяснено кратко и доходчиво.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.