Решает ли использование ссылок вместо указателей утечки памяти в C ++? - PullRequest
9 голосов
/ 22 июля 2011

Большинство утечек памяти появляются, когда указатель возвращенного объекта и программист забыл удалить его.

, например:

class my_class
{
  ...
};

my_class* func1()
{
  my_class* c = new my_class;
  return c;
}

int main()
{
  my_class* var1 = func1();
  ...
  // Programmer forgot delete the var1: delete var1;
  // -- or --
  // Doesn't know 'delete[] var1;' is correct or 'delete var1;'.
}

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

например:

class my_class
{
  ...
};

void func2(my_class* p)
{
  ...
}

int main()
{
  my_class* var3 = new my_class;

  func2(var3);

  // Does func2 deletes var3? Programmer doesn't know.
  // -- or --
  // Programmer forgot delete the var3.
}

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

Мой метод: Не используйте указатели (кроме одного места), просто используйте ссылки вместо указателей.

например:

class my_class
{
  ...
};

my_class& func1()
{
  my_class* c = new my_class; // except one place.
  return *c;
}

void func2(my_class& p)
{
  ...
}

int main()
{
  my_class& var1 = func1();
  my_class  var2 = func1();

  my_class var3;
  func2(var3);

  // There is nothing to forget.
}

Разрешает ли утечки памяти использовать ссылки вместо указателей?

Это хороший метод для устранения утечек памяти или есть более эффективные методы?


Редактировать:

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

, поскольку это новый вопрос, я задаю его отдельно.

class my_class
{
  ...
};

my_class& func()
{
  my_class* c = new my_class;
  return *c;
}

int main()
{
  my_class& var1 = func();

  // I think there is no memory leak.
}

Я спрашиваю здесь: Этот код пропускает память?(ссылки, новые, но без удаления)

Ответы [ 5 ]

7 голосов
/ 22 июля 2011

Вы не устранили никаких утечек памяти. Если вы новый, то вы должны удалить. Все, что вы сделали - разыменовали указатель, его все еще нужно удалить. Вы можете устранить утечки памяти, создавая локальные объекты и возвращая их по значению, или используя умные указатели. 99 раз из 100 я предпочитаю вариант возврата по значению.

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

5 голосов
/ 22 июля 2011

Ваш подход совсем не помогает:

Foo & magic()
{
  return * new Foo();  // dynamically allocated
}

int main()
{
  Foo x = magic();     // copied and lost
  Foo & y = magic();   // reference, still holding on
  delete &y;           // phew, and ewww
}

Вы все еще просто динамически распределяете объект и должны позаботиться о нем вручную!Фактически, мое первое использование делает копию ссылки, а затем забывает ссылку, создавая мгновенную утечку!И даже если вы все же каким-то образом сохранили ссылку, как во втором примере, она становится совершенно неуправляемой!(См. Комментарий Soap.)

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

Например:

#include <memory>

typedef std::shared_ptr<Foo> FooPtr;

FooPtr makeFoo()
{
  // return FooPtr(new Foo);       // Baby's first smart pointer
  return std::make_shared<Foo>();  // Adult approach to shared_ptr
}

int main()
{
  FooPtr pf = makeFoo();

  someFooModifier(*pf);
}
3 голосов
/ 22 июля 2011

Не возвращать необработанные указатели из функций; вставьте их в класс интеллектуальных указателей, например unique_ptr или shared_ptr. Тогда вам не нужно беспокоиться об удалении выделенного объекта.

Кроме того, во втором примере, кто удаляет объект, выделенный func1()? Тот факт, что вместо указателя вы возвращаете ссылку, не означает, что освобождение выделенной памяти будет происходить магическим образом.

2 голосов
/ 22 октября 2012

Да, есть утечка памяти. Следующий код выполняется с valgrind:

#include <iostream>

class my_class
{
    public :
    my_class(void)
    {
        std::cout << "Constructor" << std::endl;
    }
     ~my_class(void)
    {
        std::cout << "Destructor" << std::endl;
    }
};

my_class& func(void)
{
    my_class* c = new my_class;
    return *c;
}

int main(void)
{
    my_class& var1 = func();
    // Question : I think there is no memory leak.
    // Answer : Yes there is a memory leak in func().
}



/shared/TOOLS/valgrind-3.6.1/bin/valgrind --leak-check=full ./main2.exe
==13004== Memcheck, a memory error detector
==13004== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==13004== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==13004== Command: ./main2.exe
==13004==
Constructor
==13004==
==13004== HEAP SUMMARY:
==13004==     in use at exit: 1 bytes in 1 blocks
==13004==   total heap usage: 1 allocs, 0 frees, 1 bytes allocated
==13004==
==13004== 1 bytes in 1 blocks are definitely lost in loss record 1 of 1
==13004==    at 0x4A06DC7: operator new(unsigned long) (vg_replace_malloc.c:261)
==13004==    by 0x400966: func() (in /home/toto/CppTest/main2.exe)
==13004==    by 0x4009BC: main (in /home/toto/CppTest/main2.exe)
==13004==
==13004== LEAK SUMMARY:
==13004==    definitely lost: 1 bytes in 1 blocks
==13004==    indirectly lost: 0 bytes in 0 blocks
==13004==      possibly lost: 0 bytes in 0 blocks
==13004==    still reachable: 0 bytes in 0 blocks
==13004==         suppressed: 0 bytes in 0 blocks
==13004==
==13004== For counts of detected and suppressed errors, rerun with: -v
==13004== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 7 from 7)
1 голос
/ 22 июля 2011

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

my_class& func1()
{
    my_class* c = new my_class;
    return *c;
}

int main()
{
    my_class& ref = func1();
    // MUST delete, but this is ugly!
    delete &ref;
}

Вместо этого замените собственные указатели интеллектуальными указателями: std :: unique_ptr func1 () {return std ::unique_ptr (новый my_class);}

int main()
{
    auto p = func1();
    // You gain exception-safety for free
}

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

// Won't have to check for 0 in the body
void
my_func1(my_class&);

std::unique_ptr<my_class>
func1()
{
    return std::unique_ptr<my_class>(new my_class);
}

int main()
{
    auto p = func1();

    func2(*p);
}

Здесь возникает вопрос о разнице в использовании между необработанными указателями и интеллектуальными указателями .В моем ответе я упоминаю различие в вариантах использования для необработанных указателей и ссылок.

...