Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > По поводу наследования


Автор: Salatovec 25.9.2013, 10:36
Доброго всем времени суток, столкнулся с такой проблемой - вот код:

Код

class CFigure
{
public:
    virtual void Draw(void) = 0;
};

class CBox : public CFigure
{
public:
    virtual void Draw(void);
};

class CCone : public CFigure
{
public:
    virtual void Draw(void);
};

int main()
{
    CFigure *figures[2];
    
    figures[0] = new CBox;
    figures[1] = new CCone;

    figures[0].Draw();
    figures[1].Draw();

    delete figures[];

    return 0;
}


Можно ли при сохранении структуры классов создать массив фигур без указателей и конструкций аля new-delete?

Заранее спасибо за помощь!

Автор: Earnest 25.9.2013, 10:40
Без указателей в один массив разные типы загнать нельзя. А вот без delete обойтись вполне можно. Используй умные указатели, например из буста. Насчет new - если есть желание и смысл заморачиваться, можно какую-нибудь фабрику организовать. Но где-то все равно будет new, естественно.

Автор: Salatovec 25.9.2013, 10:56
Earnest, спасибо, с этим моментом понял. Тогда попробую с другой стороны зайти. При использовании такой структуры проблема возникла при реализации возможности удаления из массива выборочного элемента. Например:

Код

int main()
{
    CFigure *figures[4];
    
    figures[0] = new CBox;
    figures[1] = new CCone;
    figures[2] = new CBox;
    figures[3] = new CCone;

    delete figures[1];

    ....
}


А как дальше? Нужно же поменять размер массива. Переместить элементы 2 и 3 на место 1 и 2 соответственно. Это я победить так и не смог.

Автор: Guinness 25.9.2013, 11:06
http://en.wikipedia.org/wiki/Erase-remove_idiom

Автор: vinter 25.9.2013, 12:17
+ std::unique_ptr хранить в векторе, а не голые указатели.

Автор: Salatovec 25.9.2013, 12:37
Guinness, а можно небольшой примерчик использования?

vinter, это не буст случаем?

Автор: vinter 25.9.2013, 12:47
Код

#include <vector>
#include <memory>
#include <algorithm>
int main()
{
    std::vector<std::unique_ptr<CFigure>> figures;
    
    figures.emplace_back(new CBox);
    figures.emplace_back(new CCone);
    figures.emplace_back(new CBox);
    figures.emplace_back(new CCone);
    figures.erase(std::begin(figures) + 1);
    ....
}

Это не буст, это стандартный C++. Правда требует более-менее свежего компилятора.
P.S. я не собирал пример.

Автор: Guinness 25.9.2013, 13:20
Цитата(vinter @  25.9.2013,  13:17 Найти цитируемый пост)
std::unique_ptr 

Точно? Это же аналог auto_ptr с некоторыми улучшениями? А такой тип указателей не рекомендуется хранить в контейнерах. Лучше, std::shared_ptr( если C++11 ) или boost::shared_ptr( в более ранних версиях ).
Цитата(Salatovec @  25.9.2013,  13:37 Найти цитируемый пост)
а можно небольшой примерчик использования?

Я же вроде ссылку дал на вики с примером. Или какой-то конкретный момент непонятен?

Автор: vinter 25.9.2013, 13:27
Цитата

Точно? Это же аналог auto_ptr с некоторыми улучшениями? А такой тип указателей не рекомендуется хранить в контейнерах

Это то, чем должен был быть auto_ptr, но так и не стал в связи с отсутствием понятия "перемещение" в ранних версиях стандарта. Насчёт "не рекомендуется использовать": впервые слышу, какие аргументы? std::shared_ptr накладывает определенные накладные расходы, которые в данном случае не нужны. Если нет потребности в std::shared_ptr его использования стоит избегать, это не бесплатная сущность. В отличие от std::unique_ptr.

Автор: Guinness 25.9.2013, 14:04
Цитата(vinter @  25.9.2013,  14:27 Найти цитируемый пост)
Насчёт "не рекомендуется использовать": впервые слышу, какие аргументы?

auto_ptr ведет себя вот так:
Код

    auto_ptr<int> p1( new int(10) );
    auto_ptr<int> p2;
    p2 = p1; // p1 - null_ptr, p2 - указывает на int(10)

Судя, по описанию unique_ptr он ведет себя схожим образом:
Код

[url=http://www.cplusplus.com/reference/memory/unique_ptr/]Ссылка[/url]
unique_ptr objects own their pointer uniquely: no other facility shall take care of deleting the object, and thus no other managed
pointer should point to its managed object, since as soon as they have to, unique_ptr objects delete their managed object
without taking into account whether other pointers still point to the same object or not, and thus leaving any other pointers that
point there as pointing to an invalid location.

[url=http://www.cplusplus.com/reference/memory/unique_ptr/operator=/]Ссылка[/url]
unique_ptr assignment
The object acquires the ownership of x's content, including both the stored pointer and the stored deleter 
(along with the responsibility of deleting the object at some point). Any object owned by the unique_ptr
object before the call is deleted (as if unique_ptr's destructor was called).


Может я чего-то не допонял или не так перевел? Но вызов foreach для вектора как показано ниже приведет к печальным последствиям:
Код

vector< unique_ptr<Object> > v;
...
for_each( v.begin(), v.end(), func ); // где func - принимает объект по значению

Или я не прав?

Автор: azesmcar 25.9.2013, 14:09
Цитата(Guinness @  25.9.2013,  14:04 Найти цитируемый пост)
Или я не прав?

 smile 
std::unique_ptr как раз таки можно хранить в контейнерах.

Добавлено через 1 минуту и 45 секунд
http://en.cppreference.com/w/cpp/memory/unique_ptr
Typical uses of std::unique_ptr include:
  • providing exception safety to classes and functions that handle objects with dynamic lifetime, by guaranteeing deletion on both normal exit and exit through exception
  • passing ownership of uniquely-owned objects with dynamic lifetime into functions
  • acquiring ownership of uniquely-owned objects with dynamic lifetime from functions
  • as the element type in move-aware containers, such as std::vector, which hold pointers to dynamically-allocated objects (e.g. if polymorphic behavior is desired)

Автор: Guinness 25.9.2013, 14:21
Цитата(azesmcar @  25.9.2013,  15:09 Найти цитируемый пост)
std::unique_ptr как раз таки можно хранить в контейнерах.

Так, я там проглядел, но у unique_ptr есть следующие объявления:
Код

copy (deleted!) (9) unique_ptr (const unique_ptr&) = delete;
copy assignment (deleted!) (4) unique_ptr& operator= (const unique_ptr&) = delete;

Т.е. копирование запрещено как таковое. Получается, что мой пример с for_each мне выкинет ошибку на этапе компиляции? А контейнеры в любом случае будут возвращать ссылку на unique_ptr. Получается избегаем проблем связанных с auto_ptr.

Автор: vinter 25.9.2013, 14:21
Цитата

Судя, по описанию unique_ptr он ведет себя схожим образом:

Нет, он не имеет конструктора копирования, только конструктор перемещения, что делает его тем, чем auto_ptr так и не стал.
Вы не можете его использовать неправильно неявно, в отличие от auto_ptr
Цитата

Или я не прав?

Не правы, приведённые пример просто не соберется, по причине, которая приведена выше. 
А вот так соберётся:
Код

for_each( v.begin(), v.end(), [](unique_ptr<int>&){});

Чтобы Ваш пример собирался и работал for_each должен выглядеть как-то так:
Код

void for_each(Iter begin, Iter end, Func func)
{
     for(auto it = begin; it != end; ++it)
         func(std::move(*it));
}

Само собой, никакого std::move в стандартной версии нет ибо это бы ломало контейнеры. А так контейнеры вполне хорошо работают с объектами, которые не могут быть скопированы, а могут лишь быть перемещёнными.

Автор: Guinness 25.9.2013, 14:24
Цитата(vinter @  25.9.2013,  15:21 Найти цитируемый пост)
Чтобы Ваш пример собирался и работал for_each должен выглядеть как-то так:

Ну да, либо функция должна принимать ссылку на unique_ptr.

Автор: Salatovec 25.9.2013, 14:55
vinter, спасибо за пример, попробую в свободное от работы время.

Цитата(Guinness @  25.9.2013,  13:20 Найти цитируемый пост)
Я же вроде ссылку дал на вики с примером. Или какой-то конкретный момент непонятен? 

Не заметил ссылку, спасибо.

Автор: Guinness 25.9.2013, 16:52
Для тех кому интересна информация об умных указателях, нашёл статейки, может кому пригодится:
http://eli.thegreenplace.net/2012/06/20/c11-using-unique_ptr-with-standard-library-containers/
http://herbsutter.com/2013/05/29/gotw-89-solution-smart-pointers/

Автор: vinter 25.9.2013, 17:16
тогда еще свою http://scrutator.me/post/2012/01/18/smart-pointers.aspx оставлю тут

Автор: NoviceF 26.9.2013, 15:03
Никто не прокомментировал, но

Код

delete figures[];

Синтаксическая ошибка, так что пример вообще не должен компилиться. Но даже, если здесь использовать 

Код

delete[] figures;

Программа будет падать, т.к. это попытка удалить память, выделенную на стеке при помощи delete. Но даже если программа не упадёт, будет утечка памяти, т.к. элементы массива в коде не удаляются.

Автор: akizelokro 26.9.2013, 19:23
Цитата(Salatovec @  25.9.2013,  10:36 Найти цитируемый пост)
Можно ли при сохранении структуры классов создать массив фигур без указателей и конструкций аля new-delete?

Да в общем случае можно, но это будет неудобно (не так быстро или ещё варианты). Да и скажем, в одном из вариантов не обойтись без указателей, а в другом случае нужны ссылки.
Вообще "std::vector" яляется не более чем оболочкой для массива с переменной, указывающей количество используемых элементов в массиве. Далее, С в помощь настойчивому программисту и подобные задачи уже писались до ООП. И решались не так уж сложно. Как сделать смещение в массиве с удалением элемента - это просто. Но это уже не ООП в чистом виде.
malloc в зубы - и вот тебе уже capacity контейнера vector


А чем вызвана причина необходимости именно такого подхода? Может, сначала стоит оценить реальную мотивировку?

Автор: Salatovec 30.9.2013, 09:54
Цитата(NoviceF @ 26.9.2013,  15:03)
Никто не прокомментировал, но

Код

delete figures[];

Синтаксическая ошибка, так что пример вообще не должен компилиться. Но даже, если здесь использовать 

Код

delete[] figures;

Программа будет падать, т.к. это попытка удалить память, выделенную на стеке при помощи delete. Но даже если программа не упадёт, будет утечка памяти, т.к. элементы массива в коде не удаляются.

А как тогда должна выглядеть правильная очистка? Во всех примерах, подобного рода, память освобождали именно таким образом smile 

Автор: bsa 30.9.2013, 10:04
Salatovec, delete figures[1];
delete figures[] - синтаксически неверно, а
delete []figures можно применять только после figures = new Figure*[4];
А у тебя автоматический массив указателей. Он уничтожается тоже автоматически. Но объекты, на которые ссылаются члены массива уничтожать надо вручную.

Автор: Salatovec 30.9.2013, 13:00
bsa, правильно ли я понимаю, что нужно сделать так:

Код

for(int i = 0; i < figuresCount; i ++)
{
    delete figures[i];
}


Так?

Автор: vinter 30.9.2013, 13:08
так

Автор: bsa 2.10.2013, 22:17
Salatovec, да. Правильно.
Но всеже рекомендую использовать умные указатели ВЕЗДЕ.

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