Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Умные указатели (smart pointers), auto_ptr и т.п.


Автор: SABROG 14.4.2008, 17:55
Есть класс, который генерит древовидную структуру используя обычные указателями. Структура довольно сложная и громоздкая, я боюсь где-то могу забыть прописать удаление указателя в деструкторе класса. Посему назрела мысль использовать "умников-стрелочников". Но насколько я понял пободные смартпоинтеры действуют по принципу - вышли из области видимости - сработал деструктор и сами удалились. Но у меня ситуация такая, в конструкторе создаются указатели, но при выходе из конструктора они должны остаться валидными и удалиться только в деструкторе моего класса. Как в таком случае применить смарт-поинтеры если их участь - подохнуть точно также как локальный объект созданный во временном стеке, теряется смысл ?

Автор: Daevaorn 14.4.2008, 18:01
Всё зависит от типа умного указателя. Далеко не все общедоступные реализации "умирают" при выходе из области видимости. Например boost::shared_ptr

Автор: SABROG 14.4.2008, 18:11
Уж очень не хочется использовать boost в своей программе. К тому же я так понимаю если указатель даже и shared, то компилятор сам ну ни как не определит в какой момент мне нужно удалять его, пока я вручную не задам аналог delete в нужном месте. А это опять же сводит на нет мою идею.

Автор: Dreamer_0x01 14.4.2008, 18:15
Раз указатели создаются в конструкторе, почему бы не объявить их как переменную класса в виде умного указателя?

Автор: vinter 14.4.2008, 18:16
Цитата(SABROG @  14.4.2008,  19:11 Найти цитируемый пост)
Уж очень не хочется использовать boost в своей программе.

религия не позволяет? smile
Цитата(SABROG @  14.4.2008,  19:11 Найти цитируемый пост)
 К тому же я так понимаю если указатель даже и shared, то компилятор сам ну ни как не определит в какой момент мне нужно удалять его, пока я вручную не задам аналог delete в нужном месте.

не правильно понимаешь, когда кол-во ссылок на объект станет равным 0, тогда память будет освобождена.

Автор: SABROG 14.4.2008, 18:17
Цитата(Dreamer_0x01 @  14.4.2008,  18:15 Найти цитируемый пост)
Раз указатели создаются в конструкторе, почему бы не объявить их как переменную класса в виде умного указателя?

Нельзя, они динамические. Зависит от количества записей в базе данных. И содержит все эти указатели вектор, который является членом класса.

Цитата(vinter @  14.4.2008,  18:16 Найти цитируемый пост)
религия не позволяет?

Скорее не религия, а поговорка о пушках и воробьях.

Цитата(vinter @  14.4.2008,  18:16 Найти цитируемый пост)
не правильно понимаешь, когда кол-во ссылок на объект станет равным 0, тогда память будет освобождена.


А пример можно ?  smile

Автор: vinter 14.4.2008, 18:31
Цитата(SABROG @  14.4.2008,  19:17 Найти цитируемый пост)
А пример можно ?  

создаешь shared_ptr и инициализируешь его обычным указателем, кол-во ссылок становится равным 1, при любом копировании( в том числе и инициализирующем) кол-во ссылок инкрементируется, при выходе любой копии за границу видимости, кол-во ссылок уменьшается на 1. так вот когда кол-во ссылок становится равным 0, вызывается delete для указателя который обернут в shared_ptr.

Автор: SABROG 14.4.2008, 18:40
Цитата(vinter @ 14.4.2008,  18:31)
Цитата(SABROG @  14.4.2008,  19:17 Найти цитируемый пост)
А пример можно ?  

создаешь shared_ptr и инициализируешь его обычным указателем, кол-во ссылок становится равным 1, при любом копировании( в том числе и инициализирующем) кол-во ссылок инкрементируется, при выходе любой копии за границу видимости, кол-во ссылок уменьшается на 1. так вот когда кол-во ссылок становится равным 0, вызывается delete для указателя который обернут в shared_ptr.

А как это сработает в таком виде:

//псевдо-код
Код

class A
{
public:
    list<shared_ptr *> pointers;
    A()
   {
         foreach(...)
         {
            pointers << shared_ptr(new Object);
         }
   }
}
otherClassConstructor()
{
// oA глобальный член otherClassConstructor
oA = new A;
}
metod1ofOtherClass()
{
... = oA.pointers[3];
}


Когда поинтеры убьются, когда убьется otherClass или вызовется delete для A ?

Автор: Rififi 14.4.2008, 18:47
Цитата(SABROG @  14.4.2008,  18:17 Найти цитируемый пост)
Скорее не религия, а поговорка о пушках и воробьях.

Boost  Smart Pointer является совершенно самостоятельной библиотекой и даже не требует связывания на этапе линковки (вся функциональность содержится в заголовочных файлах)
Это к вопросу о пушках.

Автор: Fin 14.4.2008, 19:31
Можно использовать ּSTL-овские контейнеры. Только в них не заносить сами указатели, а записи. Что то типа такого
Код

   vector<string> obj; 
   //...........
   obj.push_back("mathematics");
   obj.push_back("physics");
   obj.push_back("drafting");
   obj.push_back("chemistry");
   obj.push_back("resistent of stuff");


Тогда уже сам контейнер будет заботится об удалении записей.

Автор: Mephisto 14.4.2008, 19:36
Цитата(SABROG @  14.4.2008,  18:55 Найти цитируемый пост)
Но у меня ситуация такая, в конструкторе создаются указатели, но при выходе из конструктора они должны остаться валидными и удалиться только в деструкторе моего класса

Если ты создашь вектор auto_ptr'ов, то последующий занесенный auto_ptr будет копироатся из конструктора в список. А при копировании auto_ptr класс источник теряет ссылку. Так что выйдя з апределы конструктора ты ничего не потеряешь ибо вызовется деструктор у класса указатель из которого уже извлечен.
Т.е. при копировании из одного auto_ptr в другой ты не получишь 2 ссылки на auto_ptr. В таком случае только последний классс будет иметть реально ссылку которую прибьет в деструкторе. Короче кто последний влез, того и тапки.

Добавлено через 59 секунд
Fin, ага. И туда можно вогнать auto_ptr

Автор: vinter 14.4.2008, 19:58
Цитата(SABROG @  14.4.2008,  19:40 Найти цитируемый пост)
А как это сработает в таком виде:

что то я не совсем понял твой псевдокод 

Автор: maxim1000 14.4.2008, 20:19
Цитата(SABROG @  14.4.2008,  18:40 Найти цитируемый пост)
list<shared_ptr *> pointers;

не, shared_ptr - это уже указатель (по крайней мере, по смыслу), так что достаточно list<shared_ptr>

Автор: Rififi 14.4.2008, 20:21
Цитата(Mephisto @  14.4.2008,  19:36 Найти цитируемый пост)
Если ты создашь вектор auto_ptr'ов, то

... в программе очень скоро начнутся проблемы smile
Подробности почему - читать в книгах зубров (Мейерс/Саттер)

Добавлено через 58 секунд
PS. собственно, ты дальше сам рассказал почему.

Автор: SABROG 14.4.2008, 20:22
Цитата(Mephisto @  14.4.2008,  19:36 Найти цитируемый пост)
Fin, ага. И туда можно вогнать auto_ptr 


Т.е. если вместо указателей запхнуть в контейнер auto_ptr'ы, то при удалении контейнера для каждого из объекта auto_ptr'a вызовется деструктор и освобождение памяти ?

Автор: vinter 14.4.2008, 20:23
Цитата(Mephisto @  14.4.2008,  20:36 Найти цитируемый пост)
Если ты создашь вектор auto_ptr'ов

кстати вполне возможно, что этот не скомпилируется.

Автор: Mephisto 14.4.2008, 20:56
Цитата(vinter @  14.4.2008,  21:23 Найти цитируемый пост)
кстати вполне возможно, что этот не скомпилируется. 

Кстати да, возможно. 

Автор: SABROG 14.4.2008, 21:14
Так оно и есть:

Код

    std::vector<std::auto_ptr<int> > list;
    std::auto_ptr<int> var1(new int);
    list.push_back(var1);


Цитата

C:/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/bits/stl_construct.h: In function `void std::_Construct(_T1*, const _T2&) [with _T1 = std::auto_ptr<int>, _T2 = std::auto_ptr<int>]':
C:/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/bits/stl_vector.h:560:   instantiated from `void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = std::auto_ptr<int>, _Alloc = std::allocator<std::auto_ptr<int> >]'
src\Dict.cpp:52:   instantiated from here
C:/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/bits/stl_construct.h:81: error: passing `const std::auto_ptr<int>' as `this' argument of `std::auto_ptr<_Tp>::operator std::auto_ptr_ref<_Tp1>() [with _Tp1 = int, _Tp = int]' discards qualifiers
C:/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/bits/vector.tcc: In member function `void std::vector<_Tp, _Alloc>::_M_insert_aux(__gnu_cxx::__normal_iterator<typename _Alloc::pointer, std::vector<_Tp, _Alloc> >, const _Tp&) [with _Tp = std::auto_ptr<int>, _Alloc = std::allocator<std::auto_ptr<int> >]':
C:/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/bits/stl_vector.h:564:   instantiated from `void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = std::auto_ptr<int>, _Alloc = std::allocator<std::auto_ptr<int> >]'
src\Dict.cpp:52:   instantiated from here
C:/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/bits/vector.tcc:234: error: passing `const std::auto_ptr<int>' as `this' argument of `std::auto_ptr<_Tp>::operator std::auto_ptr_ref<_Tp1>() [with _Tp1 = int, _Tp = int]' discards qualifiers


А с shared_ptr тоже самое будет ?

Автор: bsa 14.4.2008, 21:20
SABROG, auto_ptr нельзя использовать в контейнерах. Просто работа с контейнерами подразумевает нормальную логику работы оператора присваивания и конструктора копирования. А у auto_ptr эти функции являются перемещающими. Другими словами, auto_ptr надо использовать при возврате функцией ссылки на объект, чтобы он сам удалился когда будет не нужен, а также для хранения указателей на динамически конструируемые поля классов.
У boost::shared_ptr логика копирования классическая. Поэтому его можно хранить в контейнерах.

Автор: SABROG 14.4.2008, 21:38
Что-то мне подсказывает, что выцепить shared_ptr отдельно от буста будет сложно...

//shared_ptr.hpp
Код

#ifndef BOOST_SHARED_PTR_HPP_INCLUDED
#define BOOST_SHARED_PTR_HPP_INCLUDED

//
//  shared_ptr.hpp
//
//  (C) Copyright Greg Colvin and Beman Dawes 1998, 1999.
//  Copyright (c) 2001-2007 Peter Dimov
//
//  Distributed under the Boost Software License, Version 1.0. (See
//  accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//
//  See http://www.boost.org/libs/smart_ptr/shared_ptr.htm for documentation.
//

#include <boost/config.hpp>   // for broken compiler workarounds

#if defined(BOOST_NO_MEMBER_TEMPLATES) && !defined(BOOST_MSVC6_MEMBER_TEMPLATES)
#include <boost/detail/shared_ptr_nmt.hpp>
#else

#include <memory>               // for std::auto_ptr

#include <boost/assert.hpp>
#include <boost/checked_delete.hpp>
#include <boost/throw_exception.hpp>
#include <boost/detail/shared_count.hpp>
#include <boost/detail/workaround.hpp>

#include <algorithm>            // for std::swap
#include <functional>           // for std::less
#include <typeinfo>             // for std::bad_cast

#if !defined(BOOST_NO_IOSTREAM)
#if !defined(BOOST_NO_IOSFWD)
#include <iosfwd>               // for std::basic_ostream
#else
#include <ostream>
#endif
#endif

#ifdef BOOST_MSVC  // moved here to work around VC++ compiler crash
# pragma warning(push)
# pragma warning(disable:4284) // odd return type for operator->
#endif


Автор: Fin 14.4.2008, 21:43
Вот мой пример. Как то я для себя писал "умный указатель" который подсчитывает сам количество ссылок и если надо удаляет.
Код

#include <list>
#include <iostream>
#include <assert.h>

using namespace std;

template <class T>
class smart_ptr
{
   private:
      struct Tptr
      {
         int count;
         T *ptr;
         Tptr()
         {
            count=1;
            ptr=NULL;
         }
      };
      
      Tptr * ptr;
      
      void delete_ptr()
      {
         if (ptr != NULL) 
         {
            ptr->count--;
            if (ptr->count == 0) 
            {
               delete ptr->ptr;
               delete ptr;
            }
            ptr=NULL;
         }
      };
      
      void new_ptr(T *t)
      { 
         delete_ptr();
         ptr=new Tptr;
         if (ptr != NULL) ptr->ptr=t;
      };
      
      void inc_ptr(Tptr *t)
      {
         delete_ptr();
         ptr=t;
         if (ptr != NULL) ptr->count++;
      }
      
   public:
      smart_ptr() {ptr=NULL;};
      smart_ptr(T *t)  
      {
         ptr=NULL;
         new_ptr(t);
      };
      
      smart_ptr(const smart_ptr<T> & cl)
      {
         ptr=NULL;
         inc_ptr(cl.ptr);
      };
      
      ~smart_ptr()
      {
         delete_ptr();
      };
      
      smart_ptr & operator = (T *t)
      {
         if (t != NULL)
         {
            new_ptr(t);
         }
         return *this;
      };
      
      smart_ptr & operator = (smart_ptr<T> & cl)
      {
          inc_ptr(cl.ptr);
          return *this;
      };
      
      T& operator *()
      {
         assert(ptr != NULL);
         assert(ptr->ptr != NULL);
         return *ptr->ptr;
         
      };
      
      T *operator ->()
      {
         assert(ptr != NULL);
         assert(ptr->ptr != NULL);
         return ptr->ptr;         
      };
      
      T *& get()
      {
         assert(ptr != NULL);
         assert(ptr->ptr != NULL);
         return ptr->ptr;         
      };
      
};


class prob
{
public:
    prob(int i): _i(i) {cout << "Constructor: " << _i << endl;};
    prob() {_i=0; cout << "Default constructor: "<< _i << endl;};
    prob(const prob &pr) {_i=pr._i; cout << "Copy constructor: "<< _i << endl;};
    ~prob() {cout << "Destructor: " << _i << endl;};
private:
    int _i;
};


typedef smart_ptr<prob > probptr;
typedef list<probptr> ProbList;

int main()
{
    ProbList problist;
    for (int i=0; i<5; ++i) problist.push_back(probptr(new prob(i)));
    return 0;
}

получаем 
Цитата

Constructor: 0
Constructor: 1
Constructor: 2
Constructor: 3
Constructor: 4
Destructor: 0
Destructor: 1
Destructor: 2
Destructor: 3
Destructor: 4

Автор: Rififi 14.4.2008, 21:47
Цитата(SABROG @  14.4.2008,  21:38 Найти цитируемый пост)
Что-то мне подсказывает, что выцепить shared_ptr отдельно от буста будет сложно...

Это будет сложно только до того момента, пока ты не познакомишься с http://www.boost.org/doc/libs/1_35_0/tools/bcp/bcp.html

Автор: bsa 14.4.2008, 21:48
SABROG, а почему ты так боишься самого буста? Установи его и используй. Какие проблемы? Тем более, что вроде как этот shared_ptr в стандарт С++ должен войти, как и некоторые другие вещи из буста.

Автор: SABROG 14.4.2008, 21:49
Цитата(Rififi @ 14.4.2008,  21:47)
Цитата(SABROG @  14.4.2008,  21:38 Найти цитируемый пост)
Что-то мне подсказывает, что выцепить shared_ptr отдельно от буста будет сложно...

Это будет сложно только до того момента, пока ты не познакомишься с http://www.boost.org/doc/libs/1_35_0/tools/bcp/bcp.html

Уже познакомился, спс. Просто я скачал исходники через SVN, а этот BCP надо собирать...

Цитата(bsa @  14.4.2008,  21:48 Найти цитируемый пост)
SABROG, а почему ты так боишься самого буста? Установи его и используй. Какие проблемы? Тем более, что вроде как этот shared_ptr в стандарт С++ должен войти, как и некоторые другие вещи из буста. 


А потому, что программа кроссплатформенная и возможно я буду выкладывать ее исходники и мне бы не хотелось заставлять других программистов устанавливать boost только ради того, что я использую shared_ptr...

Автор: bsa 14.4.2008, 21:50
Цитата(SABROG @ 14.4.2008,  21:49)
Просто я скачал исходники через SVN

Зачем, если не секрет? Чем тебя стабильная версия не устроила?

Автор: Rififi 14.4.2008, 21:55
bsa, 
Иногда в в trunk'е появляются различные полезные классы/библиотеки которых долго ждать в релизе (как например это было с circular_buffer etc), а учитывая, насколько может затянуться релиз...

Автор: bsa 14.4.2008, 21:59
Цитата(Rififi @ 14.4.2008,  21:55)
bsa, 
Иногда в в trunk'е появляются различные полезные классы/библиотеки которых долго ждать в релизе (как например это было с circular_buffer etc), а учитывая, насколько может затянуться релиз...

Тут речь идет о shared_ptr, которому уже 100 лет в обед.  smile 

Автор: SABROG 14.4.2008, 22:02
Цитата(bsa @ 14.4.2008,  21:59)
Цитата(Rififi @ 14.4.2008,  21:55)
bsa, 
Иногда в в trunk'е появляются различные полезные классы/библиотеки которых долго ждать в релизе (как например это было с circular_buffer etc), а учитывая, насколько может затянуться релиз...

Тут речь идет о shared_ptr, которому уже 100 лет в обед.  smile

Я руководствовался такой логикой, раз SVN значит исходники, а раз исходники, значит можно выдрать shared_ptr и желательно последней версии, если он вообще менялся smile

Автор: Rififi 14.4.2008, 22:04
bsa, ну если про shared_ptr, тогда, наверное, преимуществ нет. А вообще, вотЪ smile

Автор: bsa 14.4.2008, 22:07
SABROG, буст в исходниках и поставляется. Поэтому, когда хочется по быстрее установить гимор возникает - компилится сволочь долго (не всем только shared_ptr нужен)!

Автор: SABROG 14.4.2008, 22:14
Похоже будет проще взять версию Fin'a (за что ему спасибо) чем лезть через терни к звездам.

Автор: Mephisto 14.4.2008, 22:32
Цитата(bsa @  14.4.2008,  22:48 Найти цитируемый пост)
SABROG, а почему ты так боишься самого буста? Установи его и используй. Какие проблемы? Тем более, что вроде как этот shared_ptr в стандарт С++ должен войти, как и некоторые другие вещи из буста. 

Поправте меня если я не прав. Но насколько я знаю буст как раз и анонсирует то что все их классы поддерживают только стандарт С++.

Автор: vinter 14.4.2008, 22:38
Цитата(Mephisto @  14.4.2008,  23:32 Найти цитируемый пост)
Поправте меня если я не прав. Но насколько я знаю буст как раз и анонсирует то что все их классы поддерживают только стандарт С++.

буст все же стороння либа, то что содержится там не обязательно войдет в стандарт, но по крайней мере в boost tr1 содержится то что, должно быть в std::tr1

Автор: bsa 14.4.2008, 22:58
Цитата(Mephisto @ 14.4.2008,  22:32)
насколько я знаю буст как раз и анонсирует то что все их классы поддерживают только стандарт С++.

Сам то понял, что сказал?
Я тут недавно прочитал про то, зачем собственно этот буст был создан. Смысл такой, это что-то вроде бета-версии новых компонентов стандартной библиотеки С++. Далеко не все войдет в стандатр, но наиболее часто используемые вещи войдут.

Автор: SABROG 15.4.2008, 08:35
Как я и думал "через терни к звездам", не собирается буст 1.35.0:

Цитата

C:\boost_1_35_0>bjam ^
--build-dir="c:\boost" ^
--toolset=gcc stage
...
?? ??????? ????? C:\boost_1_35_0\stage\lib\boost_graph-mgw34-mt.lib
??????????? ??????:         1.
common.hard-link stage\lib\boost_iostreams-mgw34-mt.lib
?? ??????? ????? C:\boost_1_35_0\stage\lib\boost_iostreams-mgw34-mt.lib
??????????? ??????:         1.
common.hard-link stage\lib\boost_program_options-mgw34-mt.lib
?? ??????? ????? C:\boost_1_35_0\stage\lib\boost_program_options-mgw34-mt.lib
??????????? ??????:         1.
common.hard-link stage\lib\boost_python-mgw34-mt.lib
?? ??????? ????? C:\boost_1_35_0\stage\lib\boost_python-mgw34-mt.lib
??????????? ??????:         1.
common.hard-link stage\lib\libboost_regex-mgw34-mt.lib
?? ??????? ????? C:\boost_1_35_0\stage\lib\libboost_regex-mgw34-mt.lib
??????????? ??????:         1.
...failed updating 12 targets...
...skipped 8 targets...
...updated 292 targets...


Использую mingw 3.4.5. Хотелось бы мне знать, что скрывается за этими значками вопросов....

Автор: Lazin 15.4.2008, 09:41
SABROG, для shared_ptr ничего собирать не надо...
boost я использую на работе а у нас здесь к надежности софта очень высокие требования =)

Добавлено через 2 минуты и 20 секунд
Цитата(SABROG @  15.4.2008,  08:35 Найти цитируемый пост)
Использую mingw 3.4.5. Хотелось бы мне знать, что скрывается за этими значками вопросов....


Цитата(SABROG @  15.4.2008,  08:35 Найти цитируемый пост)
...failed updating 12 targets...
...skipped 8 targets...
...updated 292 targets...


питон не установлен, boost::python поэтому не собирается, все остальное собралось =)

Автор: SABROG 15.4.2008, 10:36
Цитата(Lazin @ 15.4.2008,  09:41)
SABROG, для shared_ptr ничего собирать не надо...
boost я использую на работе а у нас здесь к надежности софта очень высокие требования =)

Дело в том, что boost не имеет собранной утилиты BCP в исходниках, а она мне нужна, чтобы выцепить shared_ptr. Конечно, как вариант, можно поискать уже собранную, но я думал будет легче собрать буст "на будущее".

Вечером буду смотреть копать дальше как питон подключать, странно, что в мануале не дали ключи к bjam'у.

Автор: UnrealMan 15.4.2008, 13:07
Цитата(Fin @  14.4.2008,  21:43 Найти цитируемый пост)
Вот мой пример. Как то я для себя писал "умный указатель" который подсчитывает сам количество ссылок и если надо удаляет.

Замечательный пример того, что велосипедам лучше предпочесть нормальные широкоизвестные либы. Твой код просто ужасен: пользователь никак не может проверить указатель на равенство нулю, не может разыменовать и скопировать оператором присваивания константный указатель, и есть случай, когда программа из-за твоего указателя может попросту упасть. Ну и инкапсуляция тут тоже хромает (см. метод get). В топку такой класс.

Автор: Fin 15.4.2008, 21:34
UnrealMan, Может и в топку. Он в свое время отработал нормально и без притензий. Я никого не заставляю использовать этот класс, просто показал, что можно работать. Так что, как говорится в одной очень известной лицензии  AS IS.

Автор: SABROG 16.4.2008, 10:30
Цитата(Lazin @ 15.4.2008,  09:41)
питон не установлен, boost::python поэтому не собирается, все остальное собралось =)

Сам питон у меня стоит, вчера проверил. Может ему исходники нужны ?

Автор: Lazin 16.4.2008, 10:55
Ему нужно переменную окружения создать PYTHON_PATH вроде-бы...
Если тебе не нужен boost::python, то можно и не собирать, все остальное и так уже собрано

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)