LINUX.ORG.RU

Смартпоинтеры в STL.


0

0

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

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

> Не указатели же на базовый класс хранить?

а что не так? в большинстве случаев ( если не использовать виртуальное наследование - что по сути редкость ) даже не надо dynamic_cast, достаточно обычного static_cast

lester ★★★★
()

а smart_ptr вроде есть в следующем стандарте

lester ★★★★
()
Ответ на: комментарий от lester

а что не так?

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

а smart_ptr вроде есть в следующем стандарте

Есть. Но как люди раньше то писали?

NightmareZ
() автор топика
Ответ на: комментарий от NightmareZ

> Не так то, что я могу насоздавать динамически этих объектов, потом схватить неожиданно исключение и получить утечку.

«неожиданные» исключения - сами по себе ошибка

Есть. Но как люди раньше то писали?


использовали готовые реализации из boost, Loki и т.п., либо писали сами

lester ★★★★
()
Ответ на: комментарий от NightmareZ
#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
() автор топика
Ответ на: комментарий от lester

«неожиданные» исключения - сами по себе ошибка

Шозабред? Ну закончилась у меня память - вылетело исключение. Ожиданно?

NightmareZ
() автор топика
Ответ на: комментарий от NightmareZ

> Шозабред? Ну закончилась у меня память - вылетело исключение. Ожиданно?

1. try/catch никто не отменял, вообще использовать исключения( в том числе не запретив их в компиляторе ) и не заботится об их обработке - это именно «Шозабред»
2. написать свой OwnedVector дело 2-х минут

lester ★★★★
()
Ответ на: комментарий от lester

1. try/catch никто не отменял, вообще использовать исключения( в том числе не запретив их в компиляторе ) и не заботится об их обработке - это именно «Шозабред»

Ну я хочу, чтобы у меня была коллекция с семантикой стека. Вышел за область видимости (не важно как) - всё удалилось само.

2. написать свой OwnedVector дело 2-х минут

Что такое OwnedVector?

NightmareZ
() автор топика
Ответ на: комментарий от NightmareZ

> throw 1; // неожиданно

вы любите себе делать сюрпризы? :) или вас ставит в тупик такая ситуация и вы не знаете как сначала «подчистить», а потом кинуть исключение?

lester ★★★★
()
Ответ на: комментарий от NightmareZ

> Ну я хочу, чтобы у меня была коллекция с семантикой стека. Вышел за область видимости (не важно как) - всё удалилось само.

auto_ptr?

Что такое OwnedVector?


«holds a list of pointers to objects, and will automatically delete the objects when they are removed from the array, or when the array is itself deleted.»

lester ★★★★
()
Ответ на: комментарий от lester

? :)

void func()
{
	std::vector<Base*> vec;
	try
	{
		vec.push_back(new Child1());
		vec.push_back(new Child2());

		// что-то делаем...

		throw 1; // неожиданно
	}
	catch(...)
	{
		for (std::vector<Base*>::iterator it = vec.begin(); it != vec.end(); it++)
			delete *it;
	}
}

NightmareZ
() автор топика
Ответ на: комментарий от lester

auto_ptr?

Не, ну так вектор у меня и так на стеке. Мне нужно чтобы содержимое было полиморфным, но тоже удалилось само. А auto_ptr в векторе юзать нельзя.

NightmareZ
() автор топика
Ответ на: комментарий от lester

«holds a list of pointers to objects, and will automatically delete the objects when they are removed from the array, or when the array is itself deleted.»

Т.е. писать обёртки над контейнерами? А стандартных средств нет?

NightmareZ
() автор топика
Ответ на: комментарий от NightmareZ

> Т.е. писать обёртки над контейнерами? А стандартных средств нет?

а чем не устраивают уже готовые реализации smart_ptr?

lester ★★★★
()
Ответ на: комментарий от NightmareZ
// что-то делаем...
for (std::vector<Base*>::iterator it = vec.begin(); it != vec.end(); it++)
    delete *it;

if( failed ) throw 1; // ожиданно
lester ★★★★
()
Ответ на: комментарий от lester

а чем не устраивают уже готовые реализации smart_ptr?

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

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

Ну это я в примере написал throw 1. Естесственно его можно переместить. Вопрос же был о том, что в реальности код может быть куда сложнее. Допустим, должен он располагаться после создания и заполнения вектора и до его очищения, и, этот код может бросать исключения.

NightmareZ
() автор топика
Ответ на: комментарий от NightmareZ

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

в любом случае лучше использовать обертку вокруг vector, а не элементов, даже такую, что просто в деструкторе сделает:

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

т.к. это явно эффективней

lester ★★★★
()
Ответ на: комментарий от lester

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

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; 
}
NightmareZ
() автор топика
Ответ на: комментарий от NightmareZ

> В идеале я бы хотел вот такого, но из стандартной поставки:

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

lester ★★★★
()
Ответ на: комментарий от lester

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

Зато он безопасен и будет работать в любом случае.

NightmareZ
() автор топика
Ответ на: комментарий от NightmareZ
std::vector<Base*> vec;
MyMegaCleaner( vec );

разве так не лучше?

lester ★★★★
()

А взять нормальный язык, с GC - религия не позволяет? Смартпоинтеры и т.п. это такая штука, которая как бы намекает, что для данной задачи надо было взять более-менее приличный высокоуровневый язык, хотя бы тот же додиез, а не ковыряться в говнеc++.

Love5an
()
Ответ на: комментарий от Love5an

А взять нормальный язык, с GC - религия не позволяет?

Позволяет. Я на шарпе пишу. Но для расширения кругозора периодически копаю другие ЯП.

Смартпоинтеры и т.п. это такая штука, которая как бы намекает, что для данной задачи надо было взять более-менее приличный высокоуровневый язык, хотя бы тот же додиез, а не ковыряться в говнеc++.

А собсна, чем тебе не устраивают нормальные смартпоинтеры на C++ (хотя б из того же буста)?

NightmareZ
() автор топика
Ответ на: комментарий от P1XeL

тогда вам обратно на C#

Данную задачу C++ тоже позволяет решать безопасно. Но, почему-то, для этого нет средств в стандартной библиотеке (нужно либо самому писать, либо использовать сторонние разработки). Меня интересует почему же так.

NightmareZ
() автор топика
Ответ на: комментарий от NightmareZ

> Данную задачу C++ тоже позволяет решать безопасно.

Меня интересует почему же так.


потому-что С++ призван обеспечить высокую скорость работы, а безопасность при этом должен обеспечить программист - это плата за скорость

lester ★★★★
()
Ответ на: комментарий от NightmareZ

Меня интересует почему же так.

читай «дизайн и эволюцию»

для этого нет средств в стандартной библиотеке

в стандартной библиотеке C++ нет почти никаких средств

нужно либо самому писать

а вот это - основная философия мира C++. впрочем, твоя проблема всегда решалась с помощью «степеней безопасности в смысле исключений» и некоторой аккуратности при проектировании

jtootf ★★★★★
()
Ответ на: комментарий от NightmareZ

>Шозабред? Ну закончилась у меня память - вылетело исключение. Ожиданно?

new умеет выдавать исключения. И set_new_handler никто не отменял

yoghurt ★★★★★
()
int f()
{
        struct ptr_vector: std::vector<Base*> {
                ~ptr_vector()
                {
                        for (std::vector<Base*>::iterator it = begin(); it != end(); it++) {
                                delete *it;
                        }
                }
        } vec;

        vec.push_back(new Child1());
        vec.push_back(new Child2());

        throw 1;

        return 0;
}

Где-то так :)

rymis ★★
()
Ответ на: комментарий от rymis
int f()
{
        struct ptr_vector: std::vector<Base*> {
                ~ptr_vector()
                {
                        for (std::vector<Base*>::iterator it = begin(); it != end(); it++) {
                                delete *it;
                        }
                }
        } vec;

        vec.push_back(new Child1());
        vec.push_back(new Child2());

        throw 1;

        return 0;
}

Где-то так :)

rymis ★★
()
Ответ на: комментарий от lester

почему нет?

Any class (or class template) from which you can derive further classes (or class templates) would, as you ask, generally have a virtual destructor, and of course a bunch of virtual member functions intended to be overridden in sub-classes. This of course means that such a class would require all the baggage that goes with supporting virtual functions, and this is bad for efficiency so is not done for STL classes/class templates.

Thus you cannot extend STL types for use polymorphically - that is you cannot in general safely use types derived from STL types through pointers or references to their base types. In the specific question you ask, because they do not declare a virtual destructor deleting a an object sub-classed from an STL base class via a pointer to the base class would not cause the sub-class part of the object to be destroyed properly and depending on the specifics of the sub-class implementation might lose memory or cause other resources problems.

Therefore in general it is more common to create, say, your own container types by wrapping your own class (or class template) around an STL container, and supplying only those operations you actually require - that is the STL container is a member of your class/class template. See for example the STL container adapters such as queue, priority_queue and stack - each of these contains a member of the container type they are adapting (passed as a parameter to the container adapter class template), they do not derive from them.

например. за публичное наследование от STL'ных классов, по-хорошему, надо отрывать три и более конечностей

jtootf ★★★★★
()
Ответ на: комментарий от jtootf

понятно - надо было мне сначала хотя-бы посмотреть на реализацию вектора, в принципе тут можно «накостылять» решение с наследованием, но не стоит; тогда наверное оптимальней всего будет использовать тот самый «VectorCleaner», который я предлагал

lester ★★★★
()
Ответ на: комментарий от gavv

а чем плох boost::ptr_vector ?

как в C++ (без всяких бустов и нового Стандарта)

jtootf ★★★★★
()
Ответ на: комментарий от rymis

Можно автоматизировать:

template <typename C>
  struct CollectionWrapper : public C {
    ~CollectionWrapper() {
       for (typename C::iterator itr = C::begin();
            itr != C::end();
            ++itr)
       { 
         delete *itr; 
       } 
    } 
  };
Absurd ★★★
()
Ответ на: комментарий от jtootf

>за публичное наследование от STL'ных классов, по-хорошему, надо отрывать три и более конечностей

Ну деление это тоже вредная операция, поскольку в знаменателе может оказаться ноль. За деление надо отрывать руки-ноги. Хотя по хорошему руки-ноги надо отрывать за создание таких языков как С++.

Absurd ★★★
()
Ответ на: комментарий от Absurd

Ну деление это тоже вредная операция, поскольку в знаменателе может оказаться ноль

наследование от STL'ных контейнеров - это деление на множестве, состоящем из одних нулей

jtootf ★★★★★
()
Ответ на: комментарий от jtootf

>наследование от STL'ных контейнеров - это деление на множестве, состоящем из одних нулей

Кто вообще может вызвать этот ужасно опасный невиртуальный деструктор, если коллекция целиком локальная?

Absurd ★★★
()

> (без всяких бустов и нового Стандарта)

Перефразирую вопрос: почему в стандарте 12-летней давности чего-то нет? Более того, стандарт языка вообще-то должен определять достаточное количество фич _языка_, чтобы можно было написать реализацию (библиотеку) чего угодно. В бусте, кстати, есть либа intrusive, это к вопросу о полиморфизме.

const86 ★★★★★
()
Ответ на: комментарий от Absurd

Кто вообще может вызвать этот ужасно опасный невиртуальный деструктор, если коллекция целиком локальная?

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

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

jtootf ★★★★★
()
Ответ на: комментарий от jtootf

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

Если лежит в private-секции класса, например. Или вообще в .cxx файле вне интерфейсов.

Absurd ★★★
()
Ответ на: комментарий от Absurd

Если лежит в private-секции класса, например. Или вообще в .cxx файле вне интерфейсов.

а потом однажды кто-то произведёт рефакторинг, или сделает в другом месте почти так же; способов выстрелить себе в ногу - чуть более чем дохрена, а всё ради чего?

jtootf ★★★★★
()
Ответ на: комментарий от jtootf

>способов выстрелить себе в ногу - чуть более чем дохрена, а всё ради чего?

С канонiчным вариантом std::vector< boost::shared_ptr<Widget> > тоже можно получить много сюрпризов. Какая-нибудь хрень с тредами или цикличные ссылки, например. Это же С++.

Absurd ★★★
()
Ответ на: комментарий от jtootf

что за предрассудки?

в generic programming наследование применяется для достижения подобных целей повсеместно и задаваться при этом вопросом о каком-то виртуальном деструкторе, мягко говоря, неуместно.

acefsm
()
Ответ на: комментарий от acefsm

в generic programming наследование применяется для достижения подобных целей повсеместно

каких целей? отстреливания себе ноги по самые гланды?

jtootf ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.