Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > Смартпоинтеры в STL.


Автор: NightmareZ 18.3.2010, 04:27
О чём думали дизайнеры STL, создавая в нём единственный тип смартпоинтеров, который при том (что он единственный) нельзя использовать в контейнерах?

Мне нужен полиморфизм для объектов в коллекции (например, в векторе). Просвятите неразумного шарписта, как в C++ (без всяких бустов и нового Стандарта) это сделать? Не указатели же на базовый класс хранить?

Код
#include <vector> 
#include <iostream> 
 
class Base 

public: 
   virtual void Do() = 0; 
}; 
 
class Child1 : public Base 

public: 
   void Do() { std::cout << "Child 1" << std::endl; } 
}; 
 
class Child2 : public Base 

public: 
   void Do() { std::cout << "Child 2" << std::endl; } 
}; 
 
void func() 

   std::vector<Base*> vec; 
   vec.push_back(new Child1()); 
   vec.push_back(new Child2()); 
 
   // что-то делаем... 
 
   throw 1; // неожиданно 
 
   for (std::vector<Base*>::iterator it = vec.begin(); it != vec.end(); it++) 
      delete *it; 
}

Автор: NightmareZ 18.3.2010, 04:53
В идеале я бы хотел вот такого, но из стандартной поставки:

Код
void func()  
{  
   std::vector<std::tr1::shared_ptr<Base>> vec;  
   vec.push_back(std::tr1::shared_ptr<Base>(new Child1()));  
   vec.push_back(std::tr1::shared_ptr<Base>(new Child2()));  
  
   // что-то делаем...  
  
   throw 1; // неожиданно и пофиг 
  
   // а этого не нужно 
   // for (std::vector<Base*>::iterator it = vec.begin(); it != vec.end(); it++)  
   //   delete *it;  
}

Автор: azesmcar 18.3.2010, 06:37
Цитата(NightmareZ @  18.3.2010,  04:27 Найти цитируемый пост)
Просвятите неразумного шарписта, как в C++ (без всяких бустов и нового Стандарта) это сделать? 

Никак, в STL нет smartpointer-ов. Непонятно в чем вопрос, если ты сам это знаешь, напиши свой shared_ptr, но я бы использовал boost.

Автор: borisbn 18.3.2010, 07:29
И в любом случае в Base нужно определить виртуальный деструктор 

Автор: NightmareZ 18.3.2010, 08:06
Цитата(borisbn @  18.3.2010,  07:29 Найти цитируемый пост)
И в любом случае в Base нужно определить виртуальный деструктор 


Виртуальный деструктор тут нафик не нужен.

Добавлено через 2 минуты и 7 секунд
Цитата(azesmcar @  18.3.2010,  06:37 Найти цитируемый пост)
Никак, в STL нет smartpointer-ов.

Как нет. А auto_ptr?

Цитата(azesmcar @  18.3.2010,  06:37 Найти цитируемый пост)
Непонятно в чем вопрос, если ты сам это знаешь, напиши свой shared_ptr, но я бы использовал boost.

Вопрос в том, как люди обходятся? Пишут своё? Почему нет в стандартной библиотеке ничего подходящего?

Автор: rgsoftware 18.3.2010, 08:18
Цитата

Вопрос в том, как люди обходятся? Пишут своё? Почему нет в стандартной библиотеке ничего подходящего? 


Ну, потому что Стандартная библиотека в нынешнем виде -- это фиксация на бумаге 1998 года, тогда ещё народ, видимо, коллективно не созрел до смартпоинтеров в коллекциях.
А потом дописали буст... Если по каким-то причинам буст не устраивает, надо просто вырезать кусок, ответственный за shared_ptr, и использовать у себя с чистой совестью smile

Автор: azesmcar 18.3.2010, 08:39
Цитата(NightmareZ @  18.3.2010,  08:06 Найти цитируемый пост)
Как нет. А auto_ptr?

имелось ввиду тех, о которых ты спрашивал. Про auto_ptr ты сам написал, и сам же сказал почему он не подходит.

Цитата(NightmareZ @  18.3.2010,  04:27 Найти цитируемый пост)
О чём думали дизайнеры STL, создавая в нём единственный тип смартпоинтеров, который при том (что он единственный) нельзя использовать в контейнерах?


Цитата(NightmareZ @  18.3.2010,  08:06 Найти цитируемый пост)
Вопрос в том, как люди обходятся? Пишут своё? Почему нет в стандартной библиотеке ничего подходящего? 

нет, почему же..люди уже давно используют буст smile
boost смело можно считать стандартной библиотекой.
STL писали давно, в нее много чего не вошло.

Цитата(borisbn @  18.3.2010,  07:29 Найти цитируемый пост)
И в любом случае в Base нужно определить виртуальный деструктор  

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

Добавлено через 13 минут и 13 секунд
Цитата(borisbn @  18.3.2010,  07:29 Найти цитируемый пост)
И в любом случае в Base нужно определить виртуальный деструктор  

подожди, чего-то не так понял (я думал речь о наследниках)..так он же виртуальный.

Автор: bsa 18.3.2010, 11:30
Цитата(NightmareZ @  18.3.2010,  08:06 Найти цитируемый пост)
Виртуальный деструктор тут нафик не нужен.
Ты в этом уверен на 100%? Потому что обычно, когда есть виртуальные методы, виртуальный деструктор нужен. Другими словами, если потомки класса Base имеют хоть один атрибут, то без виртуального диструктора твой первый пример приведет к утечкам памяти.

Напиши свой вариант shared_ptr внутри #ifdef/#else/#endif, что позволит использовать твой код юзая смартпоинтеры нового стандарта лишь задефайнив определенный макрос.

Автор: azesmcar 18.3.2010, 11:33
Цитата(bsa @  18.3.2010,  11:30 Найти цитируемый пост)
Ты в этом уверен на 100%? Потому что обычно, когда есть виртуальные методы, виртуальный деструктор нужен. Другими словами, если потомки класса Base имеют хоть один атрибут, то без виртуального диструктора твой первый пример приведет к утечкам памяти.

Мне пора в отпуск..
Цитата(azesmcar @  18.3.2010,  08:39 Найти цитируемый пост)
подожди, чего-то не так понял (я думал речь о наследниках)..так он же виртуальный.

я функцию Do за деструктор принял smile 

наконец понял о чем речь.

Цитата(bsa @  18.3.2010,  11:30 Найти цитируемый пост)
обычно, когда есть виртуальные методы, виртуальный деструктор нужен

можно сказать он нужен всегда (когда есть виртуальные методы), а тут тем более, иначе в строке
Код

for (std::vector<Base*>::iterator it = vec.begin(); it != vec.end(); it++) 
      delete *it; 

будет UB.
gcc даже warning выдает на классы, у которых есть виртуальные методы и не виртуальный деструктор.

Автор: djamshud 18.3.2010, 12:21
Сишарперы настолько суровы, что не в состоянии прикрутить к своему проекту любой готовый sharedptr, не говоря уже о том, что бы написать его самому за десять минут? Нет его в STL, сами написали, так чего сопли по форумам размазывать?

Автор: NightmareZ 18.3.2010, 13:20
Цитата(bsa @  18.3.2010,  11:30 Найти цитируемый пост)
Ты в этом уверен на 100%? Потому что обычно, когда есть виртуальные методы, виртуальный деструктор нужен. Другими словами, если потомки класса Base имеют хоть один атрибут, то без виртуального диструктора твой первый пример приведет к утечкам памяти.


Уверен. Что за атрибут? Где тут утечка?

Автор: azesmcar 18.3.2010, 13:28
Цитата(NightmareZ @  18.3.2010,  13:20 Найти цитируемый пост)
Что за атрибут? 

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

Цитата(NightmareZ @  18.3.2010,  13:20 Найти цитируемый пост)
Где тут утечка? 

скорее всего это утечка, так как не вызовется деструктор наследника, но стандарт не гарантирует определенного поведения, при удалении объекта класса, не имеющего виртуального деструктора через указатель базового.

Автор: bsa 18.3.2010, 13:36
Цитата(NightmareZ @  18.3.2010,  13:20 Найти цитируемый пост)
Уверен. Что за атрибут? Где тут утечка? 
Если мы рассматриваем конкретно тот пример, который иллюстрирует проблему, а в реальной программе у тебя есть виртуальный деструктор, то нет проблем. Считай, это напоминание.

Автор: JackYF 18.3.2010, 19:58
Как тут уже сказали, есть буст и (когда-нибудь "релизнется" же он smile) C++0x, в STL которого есть тот самый бустовский умный указатель.

Автор: GoldFinch 18.3.2010, 20:15
JackYF, в следующем году релизнется:
http://herbsutter.wordpress.com/2010/03/13/trip-report-march-2010-iso-c-standards-meeting/

Автор: NightmareZ 19.3.2010, 03:08
Цитата(azesmcar @  18.3.2010,  13:28 Найти цитируемый пост)
скорее всего это утечка, так как не вызовется деструктор наследника, но стандарт не гарантирует определенного поведения, при удалении объекта класса, не имеющего виртуального деструктора через указатель базового.


Ну так деструктор вызываться должен для того, чтобы что-то в классе удалить/почистить/закрыть/etc., а у меня же класс пустой.

Автор: azesmcar 19.3.2010, 06:34
Цитата(NightmareZ @  19.3.2010,  03:08 Найти цитируемый пост)
Ну так деструктор вызываться должен для того, чтобы что-то в классе удалить/почистить/закрыть/etc., а у меня же класс пустой. 

Повторяю еще раз, утечка памяти это частный случай реализации.
Цитата(standard)

In the first alternative (delete object), if the static type of the operand is different from its dynamic type, the
static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual
destructor or the behavior is undefined.


Автор: borisbn 19.3.2010, 07:07
Цитата
у меня же класс пустой

а Child1::vf_ptr ?

Автор: JackYF 19.3.2010, 12:44
 smile 
Цитата(GoldFinch @  18.3.2010,  19:15 Найти цитируемый пост)
JackYF, в следующем году релизнется:

Спасибо за ссылку, но это ещё ничего не значит. Изначально его планировали релизнуть до 2009, и сейчас у них тоже только планы smile

Автор: Леопольд 19.3.2010, 15:25
Цитата(azesmcar @  18.3.2010,  11:33 Найти цитируемый пост)
можно сказать он нужен всегда (когда есть виртуальные методы), а тут тем более, иначе в строке
Тут два варианта. Либо открытый виртуальный деструктор, если собираемся уничтожать объект по ссылке/указателю базового класса (интерфейса). Либо защищённый невиртуальный деструктор, он не даст этого сделать.

Автор: azesmcar 19.3.2010, 15:37
Цитата(Леопольд @  19.3.2010,  15:25 Найти цитируемый пост)
Либо защищённый невиртуальный деструктор, он не даст этого сделать. 

 smile 

так что ли?
Код

class A
{
private:
    ~A() {};
};

class B: public A
{
};

так он и этого тоже не позволит
Код

A *t = new A();
delete t;

может речь идет о защищенном наследовании? В таком случае совсем непонятно зачем там нужен полиморфизм.

Автор: Леопольд 19.3.2010, 15:38
Цитата(NightmareZ @  19.3.2010,  03:08 Найти цитируемый пост)
Ну так деструктор вызываться должен для того, чтобы что-то в классе удалить/почистить/закрыть/etc., а у меня же класс пустой. 
В реальном коде он навряд ли будет пуст. В любом случае, при разрушении объекта через указатель на базовый класс деструктор которого не виртуальный, деструктор наследника не будет вызван. Это нехорошо...

Добавлено @ 15:45
Цитата(azesmcar @  19.3.2010,  15:37 Найти цитируемый пост)
так он и этого тоже не позволит
Конечно не позволит. Подразумевалось что базовый класс абстрактный.

Добавлено @ 15:47
Цитата(Леопольд @  19.3.2010,  15:25 Найти цитируемый пост)
по ссылке/указателю базового класса (интерфейса)
Мне удобнее считать абстракный класс интерфейсом. В C# именно так и сделано, насколько я знаю.

Добавлено через 13 минут и 4 секунды
Цитата(azesmcar @  19.3.2010,  15:37 Найти цитируемый пост)
Код

class A
{
private:
    ~A() {};
};

Нет, так:
Код

class A
{
    virtual void foo() const = 0;
protected:
    ~A(){};
};

Автор: azesmcar 19.3.2010, 15:54
Цитата(Леопольд @  19.3.2010,  15:38 Найти цитируемый пост)
Конечно не позволит. Подразумевалось что базовый класс абстрактный.

понял что ты имеешь ввиду, вовсе не обязательно чтобы он был абстрактным, можно например так
Код

class A
{
protected:
    ~A() {};
};

class B: public A
{
public:
    ~B() {};
};

int main ()
{
    B *t = new B();
    delete t;
}


Цитата(Леопольд @  19.3.2010,  15:38 Найти цитируемый пост)
Мне удобнее считать абстракный класс интерфейсом. 

не совсем так, на самом деле интерфейс - это класс, который описывает только интерфейс, который нужно реализовать, т.е. имеет одни только чисто виртуальные функции.

Автор: Леопольд 19.3.2010, 15:56
Цитата(azesmcar @  19.3.2010,  15:54 Найти цитируемый пост)
вовсе не обязательно чтобы он был абстрактным, можно например так
В таком случае, A::~A() должен быть виртуальным... smile

Автор: azesmcar 19.3.2010, 15:58
Цитата(Леопольд @  19.3.2010,  15:56 Найти цитируемый пост)
В таком случае, A::~A() должен быть виртуальным... smile

нет, просто опечатка, исправил.

Автор: Леопольд 19.3.2010, 16:02
Цитата(azesmcar @  19.3.2010,  15:54 Найти цитируемый пост)
не совсем так, на самом деле интерфейс - это класс, который описывает только интерфейс, который нужно реализовать, т.е. имеет одни только чисто виртуальные функции.
Как правило. Делать одну функцию чистой а остальные обычными я бы не стал. Даже если во многих наследниках придётся писать заглушки... Хотя, если функций больше 10 и нет возможности провести рефакторинг...

Автор: azesmcar 19.3.2010, 16:03
Цитата(Леопольд @  19.3.2010,  16:02 Найти цитируемый пост)
Как правило. Делать одну функцию чистой а остальные обычными я бы не стал. Даже если во многих наследниках придётся писать заглушки... Хотя, если функций больше 10 и нет возможности провести рефакторинг...

т.е. по твоему абстрактные классы не нужны?

Автор: Леопольд 19.3.2010, 16:03
Цитата(azesmcar @  19.3.2010,  15:58 Найти цитируемый пост)
нет, просто опечатка, исправил. 
Тогда я не понял что ты имел ввиду. Разверни, будь добр...

Добавлено @ 16:05
Цитата(azesmcar @  19.3.2010,  16:03 Найти цитируемый пост)
т.е. по твоему абстрактные классы не нужны? 
По моему надо стараться избегать помещать данные и не чистые функции в абстрактные классы. По крайней мере мне лично, так проще...

Автор: azesmcar 19.3.2010, 16:06
Цитата(Леопольд @  19.3.2010,  16:03 Найти цитируемый пост)
Тогда я не понял что ты имел ввиду. Разверни, будь добр...

да тоже самое, класс, который не инстанцируется, абстрактный он или нет совершенно не важно.

Автор: Леопольд 19.3.2010, 16:07
Цитата(azesmcar @  19.3.2010,  16:06 Найти цитируемый пост)
да тоже самое, класс, который не инстанцируется, абстрактный он или нет совершенно не важно. 
Где это может понадобиться?

Автор: azesmcar 19.3.2010, 16:09
std::unary_function, std::binary_function ...

Автор: Леопольд 19.3.2010, 16:14
Цитата(azesmcar @  19.3.2010,  16:09 Найти цитируемый пост)
std::unary_function, std::binary_function
Точно, это и есть претенденты на protected деструкторы... smile

Добавлено через 1 минуту и 19 секунд
Цитата(azesmcar @  19.3.2010,  15:54 Найти цитируемый пост)
понял что ты имеешь ввиду
Ты меня понял быстрее чем я тебя... smile

Автор: NightmareZ 19.3.2010, 16:31
Цитата(Леопольд @  19.3.2010,  15:38 Найти цитируемый пост)
В реальном коде он навряд ли будет пуст.


Ну так, если он всё-таки будет пуст, виртуальный деструктор не нужен?

Автор: bsa 19.3.2010, 16:56
Цитата(NightmareZ @  19.3.2010,  16:31 Найти цитируемый пост)
Ну так, если он всё-таки будет пуст, виртуальный деструктор не нужен? 
Если в твоем коде ни один наследник класса без виртуального деструктора не имеет атрибутов, то СКОРЕЕ ВСЕГО утечек памяти не будет. Но... Могут и быть, так как в стандарте прописано, что в этом случае будет НЕОПРЕДЕЛЕННОЕ ПОВЕДЕНИЕ, а оно может зависеть и от компилятора, и от ОС, и от настроек оптимизации, и от положения солнца относительно луны (но последнее уже мало вероятно).

Автор: mes 19.3.2010, 18:14
Цитата(NightmareZ @  19.3.2010,  15:31 Найти цитируемый пост)
Ну так, если он всё-таки будет пуст, виртуальный деструктор не нужен? 

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

Автор: NightmareZ 20.3.2010, 03:14
Цитата(mes @  19.3.2010,  18:14 Найти цитируемый пост)
Позвольте узнать на чем Вы, по Вашему мнению, экономите, имея виртуальные функции и не делая деструктор виртуальным ?


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

Автор: W4FhLF 20.3.2010, 08:06
Цитата(NightmareZ @  20.3.2010,  03:14 Найти цитируемый пост)
А люди, вместо того, чтобы сказать, что в этом коде он нужен или не нужен, додумывают к коду какие-то дополнения....


Тебе совершенно понятно объяснили, что в твоём случае имеет место быть UB и данных, которые ты предоставил недостаточно. Чтобы сказать будет или нет утечка можешь засунуть релиз в дизассемблер и посмотреть, что нагенерил компилятор. Или написать ключевое слово virtual и жить спокойно. 

Автор: borisbn 20.3.2010, 08:38
сделай
Код

while ( true )
{
    Base * p = new Child1();
    delete p;
}

а потом добавь
Код
virtual ~Base(){}

и повтори то же самое

Автор: NightmareZ 20.3.2010, 12:16
Цитата(borisbn @  20.3.2010,  08:38 Найти цитируемый пост)
сделай


Сделал. И что?

Автор: borisbn 20.3.2010, 12:21
user posted image smile
по идее у тебя в первом случае (без виртуального деструктора) должна была закончиться память.

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