Подтвердить что ты не робот

Будет ли приведенный ниже код причиной утечки памяти в С++

class someclass {};

class base
{
    int a;
    int *pint;
    someclass objsomeclass;
    someclass* psomeclass;
public:
    base()
    {
        objsomeclass = someclass();
        psomeclass = new someclass();
        pint = new int(); 
        throw "constructor failed";
        a = 43;
    }
}

int main()
{
    base temp();
}

В приведенном выше коде конструктор бросает. Какие объекты будут просачиваться, и как можно избежать утечек памяти?

int main()
{
    base *temp = new base();
}

Как в приведенном выше коде? Как избежать утечек памяти после того, как конструктор выбрасывает?

4b9b3361

Ответ 1

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

Это основная причина использования интеллектуальных указателей - поскольку умные poitners являются полноценными объектами, они получат деструкторы, вызываемые во время стека исключений, и имеют возможность освобождать память.

Если вы используете что-то вроде шаблона Boost scoped_ptr < > , ваш класс может выглядеть по-другому:

class base{
    int a;
    scoped_ptr<int> pint;
    someclass objsomeclass;
    scoped_ptr<someclass> psomeclass;
    base() : 
       pint( new int),
       objsomeclass( someclass()),
       psomeclass( new someclass())

    {
        throw "constructor failed";
        a = 43;
    }
}

И у вас не будет утечек памяти (и dtor по умолчанию также очистит распределение динамической памяти).


Подводя итог (и, надеюсь, это также отвечает на вопрос о

base* temp = new base();

утверждение):

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

  • деструктор для объекта будет вызываться не.
  • деструкторы для объектов-членов, содержащихся в этом классе объектов, будут называться
  • память для объекта, который строится, будет освобождена.

Это означает, что если ваш объект владеет ресурсами, у вас есть 2 метода для очистки тех ресурсов, которые могут быть уже получены при вызове конструктора:

  • поймать исключение, освободить ресурсы, а затем реконструировать. Это может быть трудно получить правильно и может стать проблемой обслуживания.
  • использовать объекты для управления сроками ресурса ресурсов (RAII) и использовать эти объекты в качестве членов. Когда конструктор для вашего объекта генерирует исключение, объекты-члены будут иметь дескрипторы, которые будут вызваны, и у них будет возможность освободить ресурс, срок службы которого они несут.

Ответ 2

Оба новых будут просачиваться.

Назначьте адрес созданных кучей объектов интеллектуальным указателям с именем, чтобы он удалялся внутри деструктора интеллектуальных указателей, которые получают вызов при вызове исключения - (RAII).

class base {
    int a;
    boost::shared_ptr<int> pint;
    someclass objsomeclass;
    boost::shared_ptr<someclass> psomeclass;

    base() :
        objsomeclass( someclass() ),
        boost::shared_ptr<someclass> psomeclass( new someclass() ),
        boost::shared_ptr<int> pint( new int() )
    {
        throw "constructor failed";
        a = 43;
    }
};

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

int main(){
    base *temp = new base();
}

Для обычного распределения памяти с использованием (не-plcaement) нового, память, выделенная оператором new, автоматически освобождается, если конструктор генерирует исключение. С точки зрения того, зачем беспокоить освобождение отдельных членов (в ответ на комментарии к ответу Майка Б.), автоматическое освобождение применяется только тогда, когда исключение выбрасывается в конструкторе объекта, который назначается новым, а не в других случаях. Кроме того, освобожденная память - это те, которые выделены для членов объекта, а не какая-либо память, которую вы, возможно, выделили, говорят внутри конструктора. т.е. освобождает память для переменных-членов a, pint, objsomeclass и psomeclass, но не для памяти, выделенной из новых someclass() и new int().

Ответ 3

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

Эта простая программа демонстрирует это.

#include <stdio.h>


class A
{
    int x;

public:
    A(int x) : x(x) { printf("A constructor [%d]\n", x); }
    ~A() { printf("A destructor [%d]\n", x); }
};


class B
{
    A a1;
    A a2;

public:
    B()
    :   a1(3),
        a2(5)
    {
        printf("B constructor\n");
        throw "failed";
    }
    ~B() { printf("B destructor\n"); }
};


int main()
{
    B b;

    return 0;
}

Со следующим выходом (с использованием g++ 4.5.2):

A constructor [3]
A constructor [5]
B constructor
terminate called after throwing an instance of 'char const*'
Aborted

Если ваш конструктор терпит неудачу частично, вы несете ответственность за это. Хуже того, исключение может быть выбрано из конструктора базового класса! Способом борьбы с этими случаями является использование "блока функции try" (но даже тогда вы должны тщательно закодировать уничтожение вашего частично инициализированного объекта).

Правильный подход к вашей проблеме тогда будет примерно таким:

#include <stdio.h>


class A
{
    int x;

public:
    A(int x) : x(x) { printf("A constructor [%d]\n", x); }
    ~A() { printf("A destructor [%d]\n", x); }
};


class B
{
    A * a1;
    A * a2;

public:
    B()
    try  // <--- Notice this change
    :   a1(NULL),
        a2(NULL)
    {
        printf("B constructor\n");
        a1 = new A(3);
        throw "fail";
        a2 = new A(5);
    }
    catch ( ... ) {   // <--- Notice this change
        printf("B Cleanup\n");
        delete a2;  // It ok if it NULL.
        delete a1;  // It ok if it NULL.
    }

    ~B() { printf("B destructor\n"); }
};


int main()
{
    B b;

    return 0;
}

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

B constructor
A constructor [3]
B Cleanup
A destructor [3]
terminate called after throwing an instance of 'char const*'
Aborted

Вы можете по-прежнему работать с интеллектуальными общими указателями, если хотите, с дополнительным копированием. Написание конструктора, похожее на это:

class C
{
    std::shared_ptr<someclass> a1;
    std::shared_ptr<someclass> a2;

public:
    C()
    {
        std::shared_ptr<someclass> new_a1(new someclass());
        std::shared_ptr<someclass> new_a2(new someclass());

        // You will reach here only if both allocations succeeded. Exception will free them both since they were allocated as automatic variables on the stack.
        a1 = new_a1;
        a2 = new_a2;
    }
}

Удачи, Цви.

Ответ 4

Да, этот код будет утечка памяти. Блоки памяти, выделенные с использованием "новых", не освобождаются при возникновении исключения. Это часть мотивации RAII.

Чтобы избежать утечки памяти, попробуйте что-то вроде этого:

psomeclass = NULL;
pint = NULL;
/* So on for any pointers you allocate */

try {
    objsomeclass = someclass();
    psomeclass = new someclass();
    pint = new int(); 
    throw "constructor failed";
    a = 43;
 }
 catch (...)
 {
     delete psomeclass;
     delete pint;
     throw;
 }

Ответ 5

Если вы входите в конструктор, вы должны очистить все, что было до вызова, чтобы бросить. Если вы используете наследование или бросаете деструктор, вам действительно этого не должно быть. Поведение нечетное (не имеет моей стандартной поддержки, но может быть undefined?).

Ответ 6

Все, что вам нужно "новое", необходимо удалить, или вы вызовете утечку памяти. Итак, эти две строки:

psomeclass = new someclass();
pint = new int(); 

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

delete pint;
delete psomeclass;

В блоке finally, чтобы избежать утечки.

Кроме того, эта строка:

base temp = base();

Не нужно. Вам просто нужно сделать:

base temp;

Добавление "= base()" не требуется.

Ответ 7

вам нужно удалить psomeclass... Не нужно очищать целое число...

RWendi