почему ошибка полиморфных типов и вопрос очистки?

#include <iostream>
#include <string>
#include <map>
#include <vector>

class base {};
class derived1 : public base
{
    public:
        unsigned short n;
        derived1()
        {
            n = 2;
        }
};
class derived2 : public base {};

void main()
{
    // way 1
    {
        std::vector<derived1> a1;
        std::vector<derived2> a2;
        std::map<std::string, base*> b;
        a1.push_back(derived1());
        b["abc"] = &a1.at(0);
        std::cout<<(dynamic_cast<derived1*>(b.find("abc")->second))->n<<std::endl;
    }

    // way 2
    {
        std::map<std::string, base*> b;
        b["abc"] = new derived1();
        std::cout<<dynamic_cast<derived1*>(b.find("abc")->second)->n<<std::endl;
        delete dynamic_cast<derived1*>(b.find("abc")->second);
    }
}

Ошибка: « dynamic_cast : base не является полиморфным типом». Что нужно сделать, чтобы исправить это? Все ли правильно очищено как на пути 1, так и на пути 2?


person alan2here    schedule 09.01.2011    source источник
comment
main возвращаемый тип не должен быть void.   -  person Mahesh    schedule 09.01.2011
comment
Общий совет: не храните указатели на элементы вектора! Как только происходит перераспределение, эти указатели становятся недействительными! Единственное исключение: если вы можете гарантировать, что перераспределения больше не произойдет, и вы не удаляете элементы так, чтобы указатель указывал за пределы текущего размера вектора.   -  person Aconcagua    schedule 17.07.2018


Ответы (1)


Чтобы сделать Base полиморфным типом, вам нужно дать ему хотя бы одну виртуальную функцию. Самым простым в этом случае будет деструктор:

class Base {
public:
  virtual ~Base() { }
};

Относительно вашего вопроса об очистке:
Технически, в обоих случаях наблюдается некоторое неопределенное поведение, поскольку объекты, на которые ссылается карта, уничтожаются до того, как с карты удаляются указатели. Это приводит к тому, что карта, когда она уничтожается, содержит недопустимые указатели, что приводит к неопределенному поведению.
Для практических целей это не вызывает никаких проблем ни с одним известным компилятором.

В противном случае вы правильно все убираете.
Но в way2 можно сделать упрощение. Когда Base имеет виртуальный деструктор, вы можете просто сделать

delete b.find("abc")->second;

без динамического состава.

person Bart van Ingen Schenau    schedule 09.01.2011
comment
Спасибо за хорошо написанный ответ. Я удивлен и очень рад, что могу упростить строку удаления в way2. Таким образом, я могу очищать объекты по ту сторону указателей, где я не знаю всей информации о типе объекта, на который указывают. - person alan2here; 09.01.2011
comment
Висячие указатели не вызывают Undefined Behavior, если они не разыменованы. И map<std::string, base*> не разыменовывает ни один из своих указателей base* в своем деструкторе или любом другом методе. - person aschepler; 21.01.2011
comment
@aschepler: Висячие указатели также вызывают UB (хотя и с обычно безвредными последствиями), если они используются в контексте, где происходит «преобразование lvalue-to-rvalue». Такое преобразование происходит, когда map<X, Y> уничтожает свое содержимое. - person Bart van Ingen Schenau; 21.01.2011