Деструктор вызвал std :: vector ‹Тип класса› недействительный for_each

Я читал о delete[], new[], что-есть-правило- из трех, освобождение-объекты-хранимые-в-векторе, delete-vector-class-member и обнаружив

Возникло исключение: нарушение прав доступа для чтения.

Я определил базовый класс

class Person
{
private:
    std::string name;
    int age;
    static int cur_id;
public:
    Person() {}
    Person(std::string new_name, int new_age) : name(new_name), age(new_age) { cur_id += 1; }
    virtual ~Person() {}
    void setname(std::string name) { this->name = name; }
    std::string getname() { return name; }
    int getage() { return age; }
    void setage(int age) { this->age = age; }
    int get_id() { return cur_id; }

    virtual void getdata(void) = 0;
    virtual void putdata(void) = 0;
};

Производный класс

class Professor : private Person
{
private:
    int publications;
    Professor* prof;
public:
    Professor() {}
    Professor(std::string new_name, int new_age, int new_pubs) : Person(new_name, new_age) { this->publications = new_pubs; }
    ~Professor() override { delete prof; }
    void getdata() override 
    {
        std::vector<std::string> prof_input = get_input();
        std::cout << prof_input[0] << "\n";
        std::cout << std::stoi(prof_input[1]) << "\n";
        std::cout << std::stoi(prof_input[2]) << "\n";
        prof = new Professor(prof_input[0], std::stoi(prof_input[1]), std::stoi(prof_input[2]));
    }
    void putdata() override 
    {
        std::cout << prof->getname() << " " << prof->getage() << " " << prof->publications << " " << prof->get_id();
    }
};

и определили основные

int main()
{
    int size; 

    std::cin >> size;

    std::cin.ignore();

    Professor* prof = new Professor();

    std::vector<Professor> prof_vec;

    for (int i = 0; i < size; i++)
    {
        int which;
        std::cin >> which;
        std::cin.ignore();

        switch (which)
        {
        case 1: 
        {
            prof->getdata();
            prof_vec.push_back(*prof);
        }
        break;
        case 2:
        {
            // something else
        }
        break;
        }
    }

    for (auto prof : prof_vec)
    {
        prof.putdata();
    }  <<< ------------------- Things so south here

    delete prof;

    fgetc(stdin);

    return 0;
}

Я полагал, что происходит (при условии, что в векторе есть только один элемент), как только вызов putdata() вышел из блока, происходит вызов деструктора delete prof, за которым следует

this-> prof был 0xCDCDCDCD.

Я пытаюсь удалить несуществующий объект. Если я хочу убедиться, что все правильно delete, что мне делать, чтобы не было утечек памяти даже на таком простом примере?


person Mushy    schedule 18.10.2017    source источник
comment
Если вы действительно хотите гарантировать отсутствие утечек памяти современным способом C ++, используйте интеллектуальный указатель (ссылка довольно старая, интеллектуальный указатель в C ++ 11 называется std::unique_ptr).   -  person spectras    schedule 18.10.2017
comment
@spectras Я полностью с вами согласен. Мой пример предназначен для изучения правильного управления памятью, но я бы использовал std::unique_ptr, std::shared_ptr с std::make_unique() или std::make_shared() в мгновение ока.   -  person Mushy    schedule 18.10.2017
comment
Хорошо. В вашей программе for (auto prof : prof_vec) делает временную копию каждого профессора во время итерации. Это возможно, потому что вы не отключили конструктор копирования, а в вашем классе есть конструктор по умолчанию и только тривиальные нестатические члены. За исключением того, что созданный компилятором конструктор копирования просто копирует указатель, который неизбежно сработает. Используйте for (auto & prof : prof_vec), если вы не хотите их копировать. И отключив этот конструктор копирования, вы также поймаете копию push_back ().   -  person spectras    schedule 18.10.2017
comment
Прочтите, пожалуйста, минимальный воспроизводимый пример. Подавляющее большинство этого кода совершенно не имеет отношения к проблеме.   -  person Barry    schedule 18.10.2017
comment
@spectras Так Class& Class(Class const& copy) = delete?   -  person Mushy    schedule 18.10.2017
comment
@Barry Будет делать и спасибо. Меня обижают за то, что я показываю минимальные примеры с использованием надуманных конструкций, и люди в конечном итоге просят больше кода, поэтому я, как правило, немного более подробный.   -  person Mushy    schedule 18.10.2017
comment
У вас не должно быть Professor* prof; в качестве члена класса профессора, в этом нет никакого смысла. Вместо этого функция getdata() должна инициализировать текущий экземпляр.   -  person M.M    schedule 18.10.2017


Ответы (2)


Вы нарушаете правило 3/5/0:

class Professor : private Person
{
private:
    Professor* prof;
public:
    // ...
    ~Professor() override { delete prof; }
};

Если вы предоставляете одну из специальных функций-членов, вы должны предоставить все специальные функции-члены. В этом случае ваш конструктор копирования просто попарно копирует Professor*, и теперь у вас есть два разных объекта, пытающихся удалить один и тот же указатель.

Предпочитаю правило 0:

class Professor : private Person
{
private:
    std::unique_ptr<Professor> prof;
public:
    // ...
    ~Professor() = default;
};
person Barry    schedule 18.10.2017
comment
@ M.M Да, наверное, даже не заметила, что они одного типа! Просто увидел нехватку специальных участников, и моя внутренняя сирена сработала. - person Barry; 18.10.2017

Член Professor* prof не инициализируется в конструкторе Professor(), а инициализируется только в getdata();

Когда код переходит к случаю 2, если нет никакого вызова getdata(), то prof остается неинициализированным.

0xCDCDCDCD указывает на неинициализированное значение в режиме отладки.

person James Dong    schedule 18.10.2017