Как мне освободить указатели внутри структур? - PullRequest
1 голос
/ 09 ноября 2011

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

struct gnt
{
    unsigned int* num;
    unsigned int size;
    bool negative;
};

Поскольку я размещаю массивы int произвольной длины для различных переменных gnt (т. Е. k.num = new int*[j]) (для некоторого значения j), мне нужно как-то освободить их. Я не уверен, как это сделать. Я просто использую delete[] k.num; (где k - gnt)? Должен ли я беспокоиться о самой структуре?

Кроме того, в качестве дополнительного вопроса я написал рекурсивную функцию для умножения элементов в списке:

char* chain_multi(char** list, unsigned int start, unsigned int end)
{
    /***************************************************************
    This function recursively multiply every items in a list then
    return the product.
    ***************************************************************/
    unsigned int size = start - end + 1;
    if(size == 1)
        return copy_char(list[end]);
    if(size == 2)
        return multiplication(list[start], list[end]);

    int rs = start - size / 2;
    char* right = chain_multi(list, rs, end);
    char* left = chain_multi(list, start, rs + 1);
    char* product = multiplication(left, right);
    delete[] left;
    delete[] right;
    return product;
}

Даст ли это какое-либо преимущество по сравнению с выполнением без рекурсии? Я тестировал списки разных размеров (от 10 до 10000 записей), и, похоже, не было никакого преимущества в отношении времени ... Хотя рекурсивный код короче своего аналога.

Спасибо за любой вклад.

Ответы [ 4 ]

3 голосов
/ 09 ноября 2011

Поскольку вы используете c ++, вы можете поместить деструктор в структуру, чтобы сделать это автоматически для вас.Есть и другие способы, но это самый практичный:

struct gnt
{
    unsigned int* num;
    unsigned int size;
    bool negative;
    ~gnt() {delete [] num; }
};

Я бы также предложил иметь конструктор, который бы удостоверился, что num имеет значение null до его инициализации, поэтому деструктор будет безопасно работать до этого:

struct gnt
{
    unsigned int* num;
    unsigned int size;
    bool negative;
    gnt() : num(NULL) {}
    ~gnt() {delete [] num; }
};

Чтобы иметь безопасное поведение, когда экземпляры назначаются или инициализируются при создании, вам нужен конструктор копирования и оператор присваивания.Они должны скопировать значения всех нединамических элементов и создать дубликат num с тем же размером и содержимым.В этом случае также рекомендуется инициализировать все члены в конструкторе, потому что размер также должен всегда иметь допустимое содержимое, чтобы это работало.Если вы не хотите сейчас слишком усложнять вещи, просто сделайте их приватными, это заставит компилятор лаять, если вы попытаетесь (неподдерживаемо) назначить или скопировать объект:

struct gnt
{
    unsigned int* num;
    unsigned int size;
    bool negative;
    gnt() : num(NULL) {}
    ~gnt() {delete [] num; }
    private: gnt(const gnt&); gnt &operator = (gnt &);
};

Как и другиеПредполагается, что одной из альтернатив является использование std :: vector вместо необработанного указателя.Таким образом, вам не нужно беспокоиться об освобождении:

struct gnt
{
    std::vector<unsigned int> num;
    unsigned int size;
    bool negative;
};

О вопросе «Должен ли я беспокоиться о самой структуре?», Это зависит от того, как вы создали ее экземпляры.Если это было с оператором нового, да.В противном случае они будут освобождены при выходе из области видимости, как и любая другая переменная.

Наконец, в отношении рекурсии IMO редко выбирает эффективность кода.Вы должны использовать рекурсию только в том случае, если код становится проще / чище И нет опасности неблагоприятных последствий (например, переполнение стека).Если это не так, я бы всегда использовал итеративную версию.

3 голосов
/ 09 ноября 2011

Следуйте правилу:
Вы должны передать тот же адрес delete[], который вы получили от new[].
Если вы разместили только одного пользователя на бесплатном хранилище, то вам нужно будет освободить только это.

Вы выделили участника k.num, используя new [], так что да, вы должны вызывать delete [] только для него.

Кроме того, вы можете использовать std::vector вместо управления памятью с помощьюсамостоятельно (если это не какое-то дерьмовое задание, которое ограничивает вас в этом)


Для поклонников Standerdese:
Стандарт C ++ 03 § 3.7.4.2-3:

Если функция освобождения завершается выдачей исключения, поведение не определено.Значение первого аргумента, переданного функции освобождения, может быть значением нулевого указателя;если так, и если функция освобождения предоставлена ​​в стандартной библиотеке, вызов не имеет никакого эффекта.В противном случае значение, предоставляемое оператору delete(void*) в стандартной библиотеке, должно быть одним из значений, возвращаемых предыдущим вызовом оператора new(std::size_t) или operator new(std::size_t, const std::nothrow_-t&) в стандартной библиотеке, и значением, предоставляемым оператору delete[](void*) встандартная библиотека должна быть одним из значений, возвращаемых предыдущим вызовом operator new[](std::size_t) или operator new[](std::size_t, const std::nothrow_t&) в стандартной библиотеке.

1 голос
/ 09 ноября 2011

Обычным преимуществом рекурсии является простота и ясность (и, возможно, другой подход к проблеме), а не обычная скорость.На самом деле, скорее наоборот: рекурсивные реализации имели тенденцию быть заметно медленнее, чем итеративные.Современное оборудование устранило или резко уменьшило эту разницу в скорости, но для рекурсивной реализации было бы довольно необычно быть быстрее, чем итеративный аналог.

0 голосов
/ 09 ноября 2011

free подходит для любого указателя, независимо от того, является он элементом структуры или нет.Если вы пишете код на C, возможно, было бы лучше использовать malloc() и free().

Для рекурсии или нет, это зависит от контекста.Вообще говоря, рекурсия в порядке.Рекурсия немного медленнее из-за некоторых вызовов функций и передачи параметров.Проблема с рекурсией заключается в том, что если вы углубитесь в уровень рекурсии (возможно, вызов 1000 вложенных функций), вы можете заполнить стек.Это может привести к сбою программы.

...