Как безопасно удалить структуру, выделенную с новым? - PullRequest
1 голос
/ 11 мая 2019

Я пытаюсь создать немного сложную структуру данных, это простая демонстрация:

#include <iostream>
#include <vector>

struct Item;
struct Store;

struct globalEnviroment
{
     Item * data;
     globalEnviroment(Item * paramData) : data(paramData){}            
     ~globalEnviroment()
     {
        delete data;
     }
};

struct Item
{
        int id;
        std::vector<std::string>* names;

        Item() {};
        ~Item ( ) {delete names;}
};

int main ( )
{
    Item * items = new Item[3];
    items[0].names = new std::vector<std::string>();
    items[1].names = new std::vector<std::string>();
    items[2].names = new std::vector<std::string>();
    globalEnviroment * ge = new globalEnviroment(items);

    delete ge;

    return 0;
}

Я получаю

munmap_chunk (): неверный указатель

ошибка в строке delete data;.

Я не уверен, как правильно удалить объект типа globalEnviroment.Мой деструктор работает правильно?

1 Ответ

1 голос
/ 11 мая 2019

У вас есть две проблемы в деструкторе globalEnviroment

  • определение Элемент неизвестен, у вас нет предупреждения при компиляции?
  • delete data; должно быть delete [] data; (в текущем случае, когда вы создали экземпляр, в котором аргумент i является результатом new Items[...], а не * new Item)

Первая возможность - переместить определение Item до определения globalEnviroment , также исправляя удаление:

#include <vector>
#include <string>

struct Item
{
        int id;
        std::vector<std::string>* names;

        Item() {};
        ~Item ( ) {delete names;}
};

struct globalEnviroment
{
     Item * data;
     globalEnviroment(Item * paramData) : data(paramData){}            
     ~globalEnviroment()
     {
        delete [] data;
     }
};

int main ( )
{
    Item * items = new Item[3];
    items[0].names = new std::vector<std::string>();
    items[1].names = new std::vector<std::string>();
    items[2].names = new std::vector<std::string>();
    globalEnviroment * ge = new globalEnviroment(items);

    delete ge;

    return 0;
}

Компиляция и исполнение:

pi@raspberrypi:/tmp $ g++ -pedantic -Wall -Wextra c.cc
pi@raspberrypi:/tmp $ ./a.out

Исполнение под valgrind :

pi@raspberrypi:/tmp $ valgrind ./a.out
==13369== Memcheck, a memory error detector
==13369== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==13369== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==13369== Command: ./a.out
==13369== 
==13369== 
==13369== HEAP SUMMARY:
==13369==     in use at exit: 0 bytes in 0 blocks
==13369==   total heap usage: 6 allocs, 6 frees, 20,296 bytes allocated
==13369== 
==13369== All heap blocks were freed -- no leaks are possible
==13369== 
==13369== For counts of detected and suppressed errors, rerun with: -v
==13369== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 3)

Вторая возможность - переместить определение деструктора после определения Item :

#include <vector>
#include <string>

struct Item;

struct globalEnviroment
{
     Item * data;
     globalEnviroment(Item * paramData) : data(paramData){}            
     ~globalEnviroment();
};

struct Item
{
        int id;
        std::vector<std::string>* names;

        Item() {};
        ~Item ( ) {delete names;}
};

globalEnviroment::~globalEnviroment()
{
  delete [] data;
}

int main ( )
{
    Item * items = new Item[3];
    items[0].names = new std::vector<std::string>();
    items[1].names = new std::vector<std::string>();
    items[2].names = new std::vector<std::string>();
    globalEnviroment * ge = new globalEnviroment(items);

    delete ge;

    return 0;
}

Компиляция и выполнение дают одинаковый результат


Из этого

  • имея указатель (и) в своем классе (классах), вы должны позаботиться о назначении, конструкторе копирования и т. Д., Вы должны определить их.
  • конструктор Item не инициализирует names в NULL, если вы не установите поле после того, как деструктор будет иметь неопределенное поведение
  • вы должны предположить data , если инициализированы массивом Items или просто Item , но ваш код будет прекрасно компилироваться независимо от того, как вы вызываете его конструктор newItem или new Item[..] в аргументе.

Если вы хотите сделать упражнение с помощью указателей, не останавливайтесь в начале, попробуйте дальше; -)

...