Это считается утечкой памяти? - PullRequest
3 голосов
/ 03 апреля 2012

Предположим, у вас есть простой класс, подобный этому:

class foo{
private:
    int* mData;
    int  mSize;
public:
    foo(int size){
        mSize = size;
        mData = new int [mSize];
    }
    ~foo() {
        mSize = 0;
        delete [] mData;
    }
};

Тогда внутри main вы делаете:

int main () {
    static int HUGE = 100000000;
    foo a(HUGE);
    // do something useful with a
    // .
    // .
    // .
    // Now I'm done with a; I do not need it anymore ...
    foo b(HUGE);
    // do something useful with b
    // Ok we are done with b
    return 0;
}

Как видите, a больше не нужен после b, но, поскольку он создается в стеке, деструктор не будет вызываться до конца программы. Теперь я знаю, что это не то же самое, что выделение с помощью new и забывание вызывать delete, однако это все еще тратит память. Вы считаете это «утечкой памяти» или просто плохим программированием?

Кроме того, как бы вы избежали подобных ситуаций? Одним из способов было бы вручную вызвать деструктор, когда объект больше не нужен, но, помимо того, что он выглядит уродливым и незнакомым, вы столкнетесь с проблемой double free, если вы не измените деструктор на что-то вроде:

foo::~foo(){
    if (mData != NULL){
        delete [] mData;
        mData = NULL;
        mSize = 0;
    }
}

Другой способ - создать a в куче через foo *pa = new foo (HUGE), а затем вызвать delete pa, когда объект больше не нужен. Это работает, но существует опасность появления еще одной возможной утечки памяти (если вы забудете вызвать delete pa).

Есть ли лучший способ избавиться от ненужных предметов?

Ответы [ 10 ]

10 голосов
/ 03 апреля 2012

Деструкторы вызываются, когда объект выходит из области видимости. C ++ допускает произвольные области видимости внутри тел функций. Напишите свою основную функцию следующим образом:

int main () {
    static int HUGE = 100000000;

    {
        foo a(HUGE);
        // do something useful with a
        // Now I'm done with a; I do not need it anymore ...
    }

    {
        foo b(HUGE);
        // do something useful with b
        // Ok we are done with b
    }
    // etc.
    return 0;
}

Я вижу, что ваш пример упрощен, но в реальной программе не забудьте либо

  • реализовать соответствующий конструктор копирования и operator= для foo или
  • добавить объявление для конструктора частной копии и operator=, чтобы его нельзя было вызвать.
3 голосов
/ 03 апреля 2012

Просто поместите ваши огромные объекты a и b в их собственные фигурные скобки, если вы беспокоитесь о масштабах.

И технически это не утечка памяти, а очень плохое управление памятью, как вы заявили.


{
  {
    foo a(HUGE);
  }
  ...

  {
    foo b(HUGE);
  }
1 голос
/ 03 апреля 2012

Конструктор класса может также взять блок памяти, выделенный вами в функции 'main ()', в качестве параметра.Таким образом, как только «а» выполнено с использованием блока памяти, вы также можете передать его в «б».Деструктору 'foo' вообще не нужно освобождать память, и вам не нужно беспокоиться о том, чтобы тратить память или время жизни объектов вообще.

1 голос
/ 03 апреля 2012

Это не утечка памяти, потому что вы не теряете отслеживание выделенной памяти . Однако это немного неэффективно, особенно когда программа работает дольше, и этого следует избегать.

Вы можете использовать области, чтобы сократить время жизни объекта:

int main () {
    static int HUGE = 100000000;
    {
        foo a(HUGE);
        // do something useful with a
        // .
        // .
        // .
        // Now I'm done with a; I do not need it anymore ...
    }
    {
        foo b(HUGE);
        // do something useful with b
        // Ok we are done with b
    }
    return 0;
}

Кроме того, стоит пересмотреть, если эти две части кода должны быть в отдельных функциях, тогда выделенные объекты будут освобождены при возврате из функции.

1 голос
/ 03 апреля 2012

Нет, это определенно не утечка памяти.

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

Вы можете добавить ограничивающую область для принудительного освобождения памяти:

{
    foo a(HUGE);
}
{
    foo b(HUGE);
}
0 голосов
/ 04 апреля 2012

Извлечение функций для уменьшения объема. Дайте им хорошие имена:

void do_a(int amount)
{
    foo a(amount);
    // ask `a` to be useful
}

void do_b(int amount)
{
    foo b(amount);
    // ask `b` to be useful
}

int main () {
    static int HUGE = 100000000;

    do_a(HUGE);
    do_b(HUGE);

    return 0;
}
0 голосов
/ 03 апреля 2012

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

0 голосов
/ 03 апреля 2012

Считаете ли вы это "утечкой памяти"

Нет, если вы не сделаете что-то вроде longjmp в середине.

или просто плохое программирование?

Я считаю использование new [] для выделения массива в вашем классе плохой практикой программирования, потому что для этого у вас есть std :: vector.

Кроме того, как бы вы избежали подобных ситуаций?

Включить foo в сферу:

{
    foo a(HUGE);
}

, если вы не измените деструктор на что-то вроде:

delete игнорирует нулевые указатели. Деструктор вызывается только один раз, поэтому не нужно обнулять переменные. Вызов деструктора вручную - это ОЧЕНЬ ПЛОХАЯ ИДЕЯ - это не для этого. Если вы хотите переинициализировать структуру, используйте методы clear () или resize ().

Есть ли лучший способ избавиться от ненужных предметов?

Да, заключить их в области.

0 голосов
/ 03 апреля 2012

Это не утечка памяти; однако это именно тот тип использования памяти, который разработчики Firefox потратили много времени на исправление.

Scope, вероятно, самый простой способ исправить это, как подсказывает Dark Falcon . В качестве альтернативы можно переместить выделения и связанный код в отдельные функции.

Кроме того, указатели можно более безопасно обрабатывать с помощью std::auto_ptr, чтобы они освобождались при освобождении области.

0 голосов
/ 03 апреля 2012

Вы считаете это "утечкой памяти" или просто плохим программированием?

Нет, это не утечка памяти.

Как бы вы избежали подобных ситуаций?

Напишите небольшие функции, несколько строк. Ваш код будет более читабельным, а неиспользуемая переменная, размещенная в стеке, будет освобождена.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...