C++. Вылетает на повторном вызове delete[]

261
04 мая 2018, 12:45

Функция подгружает данные с файла. Перед каждой новой загрузкой пытаюсь почистить данные в массиве, чтобы подгрузить все заново. При этом на второй раз вылетает в точке delete[].

Вот и сама функция:

void uploadData(Person *& persons, int & size, std::string path) {
    std::fstream fs;
    fs.open(path, std::fstream::in);
    if (!fs.is_open()) {
        std::cerr << "Error with opening file. Try again\n";
    }
    else
    {
        if (persons != nullptr) {
            delete[] persons;
        }
        MetaData metaData;
        fs.read((char*)&metaData, sizeof(MetaData));
        size = metaData.getSize();
        persons = new Person[size];
        for (int i = 0; i < size; i++) {
            fs.read((char*)&persons[i], sizeof(Person));
        }
        std::cout << "Data was successfully uploaded\n";
    }
    fs.close();
}

А вот такая функция работает без проблем:

void check(Person *& persons) {
    if (persons != nullptr) {
        delete[] persons;
    }
    persons = new Person[2];
    persons[0].setInfo();
}

Помогите добрые люди разобраться с ситуацией. А то что-то туплю уж не первый час Код из main(), на котором крашится

int main() {
    std::string path = "Storage.txt";
    Person * a = nullptr;
    int size = 0;
    std::string todo;
    for (;;) {
        std::cout << ">>";
        std::cin >> todo;
        if (todo == "pushPerson") pushPerson(a, size);
        if (todo == "showAll") showAll(a, size);
        if (todo == "uploadData") uploadData(a, size, path);
        if (todo == "saveData") saveData(a, size, path);
    }
    system("pause");
    delete[] a;
    return 0;
}

Класс Person вот:

class Person
{
public:
    std::string getName();
    int getAge();
    int getSalary();
    void setName(std::string name);
    void setAge(int age);
    void setSalary(int salary);
    void setInfo();
    void showInfo();
private:
    int salary;
    int age;
    std::string name;
}
Answer 1

Вот это:

std::string name;

в составе класса Person делает недопустимым запись и чтение с помощью функций write и read из памяти. Потому что в name содержится, например, не сама строка, а указатель на нее. При записи вы сохраняете в файле какой-то адрес, потом считываете его - когда он уже давно не актуален, а при удалении вызов деструктора пытается удалить память по несуществующему адресу...

Такие классы нужно записывать и читать иначе - например, в вашем случае для int-поля можно писать и так, но вот name я бы писал так - сначала размер строки, потом содержимое. При чтении - читал размер, выделял буфер, в него читал строку, а потом уже инициализировал ею поле name.

Ну, или, если у вас C++17 - без промежуточного буфера, как описано тут.

Answer 2

С использованием The C++ Programming Language Special Edition 2015 Страуструпа (пункт 15.6.1 Выделение памяти под массивы):

#include <iostream>

//класс содержит только число
class Test
{
private:
    int data;
public:
    explicit Test(int a):data(a)
    {
    }
    Test():data(0){}
    void set(int a)
    {
        data = a;
    }
    void show()
    {
        std::cout<<data<<std::endl;
    }
    virtual ~Test(){}


};

void update(Test* array, int size, int times=1)
{
    if(array != nullptr)
    {
        delete[] array;
    }
    array = new Test[size];
    array[0].set(100*times); //обновление данных
    array[1].set(200*times);
}
int main() {
    auto p = new Test[2];
    p[0].set(1);
    p[1].set(2);

    update(p, 2); // в первый раз умножаются данные на дефолт-значение, на 1
    p[0].show();
    update(p, 2, 2); // теперь на 2
    p[0].show();
    //Доказательство, что данные обновлены
    delete p;
    return 0;
}

Здесь представлен примитивно принцип работы Вашей программы. В книге в указанном пункте показывается такой пример инициализации массива:

    void f(int s)
{
    Employee* p = new Employee[s];
    //... 
    delete[] p;
}

У Вас же массив инициализируется нулевым указателем:

Person * a = nullptr;

(Так же не забывайте про виртуальный деструктор, если Вы будете наследовать от этого класса)

READ ALSO
Указатель на ссылку?

Указатель на ссылку?

Имеется такое определение ф-ции:

286
Переход с boost signal к boost signal 2

Переход с boost signal к boost signal 2

Я собираюсь использовать boost::signal для того, чтобы после принудительного завершения программы (ctrl+с) сохранялись данные в БД,закрывались сокеты...

237
Задача на С++ &ldquo;Книга&rdquo;

Задача на С++ “Книга”

Миша иногда читал книгиНо и это он делал странным способом: начинал он всегда с первой страницы и читал ежедневно ровно столько страниц, сколько...

272
Ошибка исполнения. Проблема с векторами. c++

Ошибка исполнения. Проблема с векторами. c++

Я пытаюсь создать вектор состоящий из векторов целых чисел, где в каждом из N элементов, есть три различных элементаПри запуске данного кода...

259