Выделяется ли память новым когда-либо автоматически? - PullRequest
5 голосов
/ 19 апреля 2009

Я на 99% уверен, что ответом на это является ослепление нет . Пожалуйста, подтвердите мое предположение, что следующий код приведет к утечке памяти.

Data &getData()
{
    Data *i = new Data();
    return *i;
}

void exampleFunc()
{
    Data d1 = getData();
    Data d2;

    /* d1 is not deallocated because it is on the heap, and d2 is
     * because it is on the stack. */
}

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

Обновление 1:

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

Data &getData()
{
    Data *i = new Data();
    return *i;
}

void exampleFunc()
{
    // Does copying occur here?
    Data &d1 = getData();

    // Does this deallocate the memory assigned to the pointer?
    delete &d;
}

Обновление 2:

Полагаю, что для ответа на мой собственный вопрос (в обновлении 1) следующий код доказывает, что назначение ссылки на ссылку не приводит к копированию ...

#include <iostream>
#include <string>

using namespace std;

class Data
{
public:
    string mName;

    Data(const string &name) : mName(name)
    { cout << mName << " default ctor" << endl; }

    Data(const Data& other)
    {
        mName = other.mName + " (copy)";
        cout << mName << " copy ctor" << endl;
    }

    ~Data()
    { cout << mName << " dtor" << endl; }

    static Data &getData(const string &name)
    {
        Data *d = new Data(name);
        return *d;
    }
};

int main()
{
    cout << "d1..." << endl;
    Data d1 = Data::getData("d1");

    cout << "d2..." << endl;
    Data d2("d2");

    cout << "d3..." << endl;
    Data &d3 = Data::getData("d3");

    cout << "return..."  << endl;
    return 0;
}

Дает следующий результат ...

d1...
d1 default ctor
d1 (copy) copy ctor
d2...
d2 default ctor
d3...
d3 default ctor
return...
d2 dtor
d1 (copy) dtor

Спасибо Эрику Мелски за отличный ответ (мой код в обновлении 2 - модифицированная копия его exmaple кода).

Ответы [ 7 ]

24 голосов
/ 20 апреля 2009

На самом деле оба d1 и d2 будут освобождены, потому что они оба находятся в стеке. То, что не освобождено - это объект Data, который вы выделили в своей функции getData(). Вы можете увидеть это немного яснее, если вы добавите в свой класс Data инструментарий для конструкторов и деструкторов. Например:

class Data {
public:
    Data() { cout << "Data default ctor" << endl; }
    Data(const Data& other) { cout << "Data copy ctor" << endl; }
    ~Data() { cout << "Data dtor" << endl; }

    static Data& getData()
    {
        Data *i = new Data();
        return *i;
    }
};

Обратите внимание, что я явно объявил конструктор копирования для Data. В вашем примере вы неявно вызываете этот конструктор, когда делаете Data d1 = getData();, и я подозреваю, что именно отсюда и происходит ваше замешательство. Теперь, если я запускаю эту простую программу:

#include <iostream>
using namespace std;

int main(int argc, char *argv[])
{
    Data d1 = Data::getData();
    Data d2;

    return 0;
}

Вывод выглядит следующим образом:

Data default ctor
Data copy ctor
Data default ctor
Data dtor
Data dtor

Построчно, вот что вы видите:

  1. Конструктор по умолчанию вызывается при выделении нового Data в getData().
  2. Конструктор копирования вызывается для создания d1 из динамически выделенного Data, который вы только что создали.
  3. Конструктор по умолчанию вызывается для создания d2.
  4. Деструктор вызывается для d2, когда он выходит из области видимости в конце main().
  5. Деструктор вызывается для d1, когда он выходит из области видимости в конце main().

Обратите внимание, что есть три вызова конструктора, но только два вызова деструктора, что указывает на то, что вы пропустили ровно один Data объект.

Надеюсь, это поможет,

Эрик Мелски

8 голосов
/ 19 апреля 2009

d1 является стековым объектом, созданным с использованием копии, возвращенной из getData(). d1 освобождается, но объект, созданный в getData(), просочился.

3 голосов
/ 20 апреля 2009

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

3 голосов
/ 20 апреля 2009

d1 и d2 оба являются объектами стека, поэтому будут уничтожены в конце их области видимости, проблема в том, что getData() создает новый объект кучи, который никогда не удаляется. d1 инициализируется при копировании из ссылки на этот объект кучи, и d1 сам будет корректно уничтожен в конце exampleFunc, но объект кучи, созданный при каждом вызове getData(), не будет удален.

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

Работает, но не очень хороший интерфейс:

Data& d = getData();
delete &d;
1 голос
/ 20 апреля 2009

Это типичный способ мышления программистов на Java / C #.

В C ++ вы можете использовать типы значений гораздо больше, чем вы думаете:

Data getData()
{
    Data i;
    return i;
}

void exampleFunc()
{
    Data d1 = getData();
    /* d1 is constructed here and destroyed */
    Data d2;

}

Если вы хотите вернуть указатель, например, когда у вас есть какой-то производный класс, который просто использует умный указатель, такой как auto_ptr, который допускает перемещение владельца:

auto_ptr<Data> getData()
{
    auto_ptr<Data> i(new Data());
    return i;
}

void exampleFunc()
{
    auto_ptr<Data> d1 = getData();
    /* now d1 is destroyed when goes out of scope */
    Data d2;
}
0 голосов
/ 20 апреля 2009

Если быть точным, ответ на ваш вопрос - да.

В этом коде:

int main()
{
Data* d = new Data();
return 0;   //end of execution
}

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

Другими словами, все данные, которые были выделены программой, которая завершила выполнение, но не была перераспределена им (программой), будут де-выделены ОС после (не во время) выполнения.

0 голосов
/ 20 апреля 2009

Вы правы в этом. Вы должны явно освободить память, используя delete или delete [].

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