Войти
ПрограммированиеФорумОбщее

[C++]<list> Полиморфность дохнет в stl контейнере.

Страницы: 1 2 3 4 Следующая »
#0
(Правка: 11:06) 9:57, 7 окт. 2019

есть вот такой пример:

#include <iostream>
#include <list>
#include <iterator>

class A
{
public:
    A() : val(888){}
    int val;
    virtual void show(){ std::cout << "virtual class A\n";}
};

class B : public A
{
public:
    B(int v) : val(v){}
    int val;
    void show(){ std::cout << "class B\n";}
};

class C : public A
{
public:
    C(int v) : val(v){}
    int val;
    void show(){ std::cout << "class C\n";}
};

Далее:

// Вариант первый: из B и из C вызывается виртуальный метод(что есть не то что нужно)
inline void test_bad()
{   std::list<A> mylist;

    for(int i = 0; i < 10; ++i)
    {   mylist.push_back(B(i));
        mylist.push_back(C(i++));
    }

    for (auto it = mylist.begin(); it != mylist.end(); it++)
    {   std::cout << it->val << " ";
        it->show();
        std::cout << "\n";
    }


    auto it = mylist.begin();
    mylist.erase(it);

    std::cin.get();
};

А вот так вроде работает:

inline void test_good()
{   std::list<A*> mylist;

    for(int i = 0; i < 10; ++i)
    {   A* p = new B(i);
        mylist.push_back(p);
           p = new C(i++);
        mylist.push_back(p);
    }

    for (auto it = mylist.begin(); it != mylist.end(); it++)
    {   std::cout << (*it)->val << " ";
        (*it)->show();
        std::cout << "\n";
    }

    // Выглядит это страшненько.
    auto it = mylist.begin();
    A* p = *it;
    mylist.erase(it);
    delete p;


    std::cin.get();
};

Вопрос: а как в таких случаях делать правильно?
Посоветуйте, пжлста, тру-идиому.


#1
10:03, 7 окт. 2019

Все правильно. В первом случае так и должно работать.

#2
10:03, 7 окт. 2019

правильно не использовать полиморфность

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

#3
10:10, 7 окт. 2019

Полиморфночть работает для указателей и для ссылок. У тебя в первом случае в контейнере сами объекты лежат, причём типа А. Компилятор никак не сможет понять, что изначально ты туда пытался пихать B и С.

#4
10:11, 7 окт. 2019

Деструктор виртуальным надо сделать, иначе действительно страшненько.
Деструкторы ни B, ни C у тебя не выполняются. В данном примере оно не убьет, но если там будут более развитые классы - это фатально.
Да и копирование надо либо реализовать, либо заблокировать. Не дай бог оно где-то скопируется в варианте "по умолчанию".

#5
(Правка: 11:09) 10:54, 7 окт. 2019

AMM1AK
> Все правильно. В первом случае так и должно работать.
а разве в узле листа не происходит динамическое выделения(аля new) памяти под добавляемый объект?
и сам нод содержит собсно указатель на энтот объект.

походу, нет: блин, ну не на стеке же лист хранит объект?

update:
исправил, я имел ввиду это
// Вариант первый: из А и из B из B и из C вызывается виртуальный метод(что есть не то что нужно)
сори (

#!
> правильно не использовать полиморфность
#!
> наследование не нужно
даешь тру-СИ программирование?

#!
> или не мешать их в одну кучу
тут интересно, но у меня бархет щас, и это скорее всего другой вопрос:
как размещать совместно используемые поля-методы?

AMM1AK
> не сможет понять, что изначально ты туда пытался пихать B и С
неясно, как это не может  понять: было же:
mylist.push_back(B(i))
а не
mylist.push_back(A())

Zab
> если там будут более развитые классы
да все верно, правила 0,3,5 щас в игнор, чтобы выхолостить саму проблему.

Так всё же, других вариантов нет? в контексте этого упрощенного случая?

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

int main()
{   classTest test;
    return 0;  
}
Всё! В мейне больше ничего нет, весь остальной код в классах.
объект на стеке может влиять по остальным юзер-типам, как они будут создаваться далее?

#6
(Правка: 11:18) 11:17, 7 окт. 2019

Клапауций
Дело в том, что в первом варианте в нодах листа ты хранишь реально объекты типа А. Когда ты делаешь new В, то выделяешь память под объект В, да! Но потом push_back копирует ее в нод. Посколько В - это наследник А, то срабатывает приведение типов и В обрезается до А и кладётся в нод. Все, потом хоть убейся, обратно ты В из нода  не получишь. С типом С аналогично. С ссылками и указателями история абсолютно другая. Указатель может быть типа А*, но реально там может находиться объект типа В или С. И благодаря полиморфности, ты как раз можешь дергать override методы.
Думаю, тебе стоит освежить знания по с++ на досуге.

#7
(Правка: 11:45) 11:40, 7 окт. 2019

AMM1AK
> Все, потом хоть убейся, обратно ты В из нода не получишь
+1 за четкость и категоричность )

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

AMM1AK
> В обрезается до А
это ваще жесть...
сначала выделили, потом обрезали, ну в принципе логика есть,
stl не поддерживает полиморфность?

а что было бы логичнее:
каждый нод узла хранить указатель на объект, ну, и пусть этот указатель будет типа А,
а тело в куче уже то которое заказали.


AMM1AK
> Думаю, тебе стоит освежить знания по с++ на досуге
я тоже так думаю, и как раз этим щас с вами занимаюсь :)

#8
(Правка: 11:44) 11:43, 7 окт. 2019

Клапауций
> даешь тру-СИ программирование?
типа того, оно к тому же быстрее исполняется как правило, а уж компилируется, не сравнить с лапшой из шаблонов и ООП

> как размещать совместно используемые поля-методы?
очевидно выносить эти блоки в отдельные структуры и просто обращаться к ним из разных функций

#9
(Правка: 12:02) 11:46, 7 окт. 2019

#!
> очевидно выносить эти блоки в отдельные структуры и просто обращаться к ним из
> разных функций
офтоп, барин. :)

разумеется процедурная парадигма в чистом виде, что может быть проще?! ...
для её понимания и использования, ... но до поры, до времени.
это как асм есть самый лёгкий и простой язык программирования!
#10
11:52, 7 окт. 2019

Клапауций

Контейнеры C++ хранят объекты inplace, поэтому там идёт глубокое копирование туда-сюда если что и копируются тут огрызки объектов помещающиеся в A.
Хранить надо указатели и нетрудно при этом заметить, что сама идея списка как структуры данных при этом идёт по полной борозде с пританцовыванием.
Выход есть - интрузивные списки.

#11
11:53, 7 окт. 2019

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

#12
12:05, 7 окт. 2019

1 frag / 2 deaths
> Наверняка в бусте такой есть.

Конечно - boost/intrusive/list.hpp

#13
(Правка: 12:11) 12:06, 7 окт. 2019

1 frag / 2 deaths
> Вообще лист, способный хранить полиморфы, технически реализовать нетрудно
это я сказал что не может, а вдруг всё же может, поэтому и мой вопрос.
пример тож мой, не из книжек.

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

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

=A=L=X=
> Контейнеры C++ хранят объекты inplace, поэтому там идёт глубокое копирование
> туда-сюда если что и копируются тут огрызки объектов помещающиеся в A.
> Хранить надо указатели и нетрудно при этом заметить, что сама идея списка как
> структуры данных при этом идёт по полной борозде с пританцовыванием.
+1 спасибо!

#14
12:12, 7 окт. 2019

Клапауций
> пример тож мой
ладно, ещё маленький оффтоп, возможно именно list тебе не нужен, пока std::deque тут уместнее

Страницы: 1 2 3 4 Следующая »
ПрограммированиеФорумОбщее