Вызывает ли std :: fill () до того, как std :: erase () вызывает неопределенное поведение? - PullRequest
1 голос
/ 05 мая 2019

Я работаю над программой на C ++ 11, где безопасность важна, и моя задача - установить 0 используемой памяти после ее удаления.

У меня есть std::map отображение от int до std::vector из указателя на класс . У меня есть индекс в std::map и указатель на экземпляр, который я хотел бы удалить.

Следующий код выдает желаемый результат, однако я не уверен, что это правильно сформированный код (или я бы сказал, что не уверен, что этот код в порядке или нет).

У меня есть 2 вопроса.

  1. Если следующий код в порядке,
  2. Он может быть скомпилирован только с -fpermissive, я не понимаю сообщение об ошибке компилятора.
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>


class MyClass
{
    private:
        int num;
    public:
        MyClass(int num) { this->num = num; }
        int GetNum() const { return this->num; }
};


void PrintWorkingData(const std::map<int, std::vector<MyClass*>>& working_data, int idx)
{
    std::cout << "working_data[" << idx << "] is an std::vector, size: " << working_data[idx].size() << ", containing the following items: " << std::endl;
    for (std::vector<MyClass*>::const_iterator it = working_data[idx].begin(); it != working_data[idx].end(); it++)
    {
        std::cout << "(*it)->GetNum() = " << (*it)->GetNum() << std::endl;
    }
}


int main()
{
    MyClass* DeleteMyClass;

    std::map<int, std::vector<MyClass*>> working_data;
    working_data[0].push_back(new MyClass{4});
    working_data[0].push_back(new MyClass{7});
    working_data[1].push_back(new MyClass{11});

    // the origonal code isn't like this; let's suppose
    // we stored in the DeleteMyClass pointer the MyClass pointer
    // that we would like to delete
    working_data[1].push_back(DeleteMyClass = new MyClass{22});

    working_data[1].push_back(new MyClass{33});
    working_data[2].push_back(new MyClass{1000});

    PrintWorkingData(working_data, 0);
    PrintWorkingData(working_data, 1);
    PrintWorkingData(working_data, 2);
    PrintWorkingData(working_data, 3);

    // so our task is to delete DeleteMyClass object from working_data[DeleteItemIndex]
    // and fill with 0 where it was stored
    int DeleteItemIndex = 1;

    std::vector<MyClass*>::iterator pos = std::find(working_data[DeleteItemIndex].begin(), working_data[DeleteItemIndex].end(), DeleteMyClass);
    if (pos == working_data[DeleteItemIndex].end())
    {
        std::cout << "Error: The item does not present in the working_data" << std::endl;
    }
    else
    {
        std::fill(pos, pos + 1, 0);
        working_data[DeleteItemIndex].erase(pos);
        delete DeleteMyClass;
        std::cout << "The object successfully deleted" << std::endl;
    }

    PrintWorkingData(working_data, 0);
    PrintWorkingData(working_data, 1);
    PrintWorkingData(working_data, 2);
    PrintWorkingData(working_data, 3);

    return 0;
}

1 Ответ

0 голосов
/ 05 мая 2019

Установка значения указателя на nullptr не изменяет данные, на которые он указывает.Стирание элемента из вектора перезапишет этот элемент всеми последующими в векторе, оставляя (в этом случае) второй указатель в выделенной памяти (за пределами размера вектора) до последнего элемента в векторе.

Чтобы стереть память, занятую объектом, на который указывает DeleteMyClass, вам придется обрабатывать уничтожение объекта и освобождение памяти отдельно.Это не обязательно легко или просто, поскольку могут быть нюансы (обработка исключений, массивы и не-массивы), которые необходимо учитывать.Вам также следует помнить, что можно проверить память запущенного процесса и просмотреть данные, которые вы пытаетесь удалить, в то время как объект, который его использует, является действующим.

Вот несколько подходов, которые могутработа для вас.

Один из способов сделать это - вручную вызвать деструктор, очистить память и освободить ее.

DeleteMyClass->~MyClass();
memset(DeleteMyClass, 0, sizeof(*DeleteMyClass));
delete (void *) DeleteMyClass;

Приведение к вызову удаления необходимоизбегайте вызова деструктора, а число очищаемых байтов использует тип DeleteMyClass, который будет неверным, если на него указывает класс, производный от MyClass.

Другой альтернативой является использование размещение нового с уже выделенным буфером памяти и специальным освобождающим устройством (после ручного вызова деструктора) для освобождения памяти.

Третья возможность - использовать custom new*Функции 1022 * и delete, либо для этого конкретного класса, либо для всего мира.

...