По поводу наследования

 
0
 
C++
ava
Salatovec | 25.09.2013, 11: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?

Заранее спасибо за помощь!
Comments (23)
ava
Earnest | 25.09.2013, 10:40 #
Без указателей в один массив разные типы загнать нельзя. А вот без delete обойтись вполне можно. Используй умные указатели, например из буста. Насчет new - если есть желание и смысл заморачиваться, можно какую-нибудь фабрику организовать. Но где-то все равно будет new, естественно.
ava
Salatovec | 25.09.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 соответственно. Это я победить так и не смог.
ava
vinter | 25.09.2013, 12:17 #
+ std::unique_ptr хранить в векторе, а не голые указатели.
ava
Salatovec | 25.09.2013, 12:37 #
Guinness, а можно небольшой примерчик использования?

vinter, это не буст случаем?
ava
vinter | 25.09.2013, 12:47 #

#include <vector>
#include <memory>
#include <algorithm>
int main()
{
    std::vector<std::unique_ptr> 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. я не собирал пример.
ava
Guinness | 25.09.2013, 13:20 #
Цитата (vinter @  25.9.2013,  13:17 findReferencedText)
std::unique_ptr 

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

Я же вроде ссылку дал на вики с примером. Или какой-то конкретный момент непонятен?
ava
vinter | 25.09.2013, 13:27 #
Цитата


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


Это то, чем должен был быть auto_ptr, но так и не стал в связи с отсутствием понятия "перемещение" в ранних версиях стандарта. Насчёт "не рекомендуется использовать": впервые слышу, какие аргументы? std::shared_ptr накладывает определенные накладные расходы, которые в данном случае не нужны. Если нет потребности в std::shared_ptr его использования стоит избегать, это не бесплатная сущность. В отличие от std::unique_ptr.
ava
Guinness | 25.09.2013, 14:04 #
Цитата (vinter @  25.9.2013,  14:27 findReferencedText)
Насчёт "не рекомендуется использовать": впервые слышу, какие аргументы?

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 - принимает объект по значению

Или я не прав?
ava
azesmcar | 25.09.2013, 14:09 #
Цитата (Guinness @  25.9.2013,  14:04 findReferencedText)
Или я не прав?


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

added later:
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)
ava
Guinness | 25.09.2013, 14:21 #
Цитата (azesmcar @  25.9.2013,  15:09 findReferencedText)
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.
ava
vinter | 25.09.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 в стандартной версии нет ибо это бы ломало контейнеры. А так контейнеры вполне хорошо работают с объектами, которые не могут быть скопированы, а могут лишь быть перемещёнными.
ava
Guinness | 25.09.2013, 14:24 #
Цитата (vinter @  25.9.2013,  15:21 findReferencedText)
Чтобы Ваш пример собирался и работал for_each должен выглядеть как-то так:

Ну да, либо функция должна принимать ссылку на unique_ptr.
ava
Salatovec | 25.09.2013, 14:55 #
vinter, спасибо за пример, попробую в свободное от работы время.

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

Не заметил ссылку, спасибо.
ava
Guinness | 25.09.2013, 16:52 #
Для тех кому интересна информация об умных указателях, нашёл статейки, может кому пригодится:
unique_ptr
unique_ptr vs shared_ptr
ava
vinter | 25.09.2013, 17:16 #
тогда еще свою статью оставлю тут
ava
NoviceF | 26.09.2013, 15:03 #
Никто не прокомментировал, но


delete figures[];

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


delete[] figures;

Программа будет падать, т.к. это попытка удалить память, выделенную на стеке при помощи delete. Но даже если программа не упадёт, будет утечка памяти, т.к. элементы массива в коде не удаляются.
ava
akizelokro | 26.09.2013, 19:23 #
Цитата (Salatovec @  25.9.2013,  10:36 findReferencedText)
Можно ли при сохранении структуры классов создать массив фигур без указателей и конструкций аля new-delete?

Да в общем случае можно, но это будет неудобно.
Вообще "std::vector" яляется не более чем оболочкой для массива с переменной, указывающей количество используемый элементов в массиве. Далее, С в помощь настойчивому программисту и подобные задачи уже писались до ООП. И решались не так уж сложно.
ava
Salatovec | 30.09.2013, 09:54 #
Цитата (NoviceF @ 26.9.2013,  15:03)
Никто не прокомментировал, но





delete figures[];



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





delete[] figures;



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

А как тогда должна выглядеть правильная очистка? Во всех примерах, подобного рода, память освобождали именно таким образом smile 
ava
bsa | 30.09.2013, 10:04 #
Salatovec, delete figures[1];
delete figures[] - синтаксически неверно, а
delete []figures можно применять только после figures = new Figure*[4];
А у тебя автоматический массив указателей. Он уничтожается тоже автоматически. Но объекты, на которые ссылаются члены массива уничтожать надо вручную.
ava
Salatovec | 30.09.2013, 13:00 #
bsa, правильно ли я понимаю, что нужно сделать так:


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


Так?
ava
vinter | 30.09.2013, 13:08 #
так
ava
bsa | 02.10.2013, 22:17 #
Salatovec, да. Правильно.
Но всеже рекомендую использовать умные указатели ВЕЗДЕ.
Please register or login to write.
Firm of day
Вы также можете добавить свою фирму в каталог IT-фирм, и публиковать статьи, новости, вакансии и другую информацию от имени фирмы.
Подробнее
Contributors
advanced
Submit