Рассмотрим одну из основных задач, встречающихся в программах на C++. Пусть требуется вернуть объект из функции.
Проще всего сделать копию:
class MyClass {
// …
};
MyClass func1() {
return MyClass();
}
Это работает, но если объект большой, то мы теряем время на копирование. Лучше вернуть значение через указатель на объект:
MyClass* func2() {
return new MyClass();
}
Оператор new
выделяет память, инициализирует объект и возвращает его адрес. Вот как можно использовать эту функцию:
MyClass* c = func2();
// …
delete c;
Обратите внимание, что мы явно вызываем оператор delete
, когда объект, на который указывает указатель, больше не нужен. После вызова объект разрушается, а память помечается, как незанятая.
Мебель из дерева на заказ здесь еще больше.
Если память не освобождать, то можно всю ее израсходовать. В этом случае новые объекты создать не получится. Но не бойтесь: после завершения работы приложения (возможно, аварийного) память в любом случае освобождается и попадает под управление операционной системы.
У вас мог возникнуть вопрос: "А зачем создавать объект с помощью new
, если можно вернуть адрес объекта"? Примерно так:
MyClass* func3() {
MyClass c;
return &c;
}
Этот код скомпилируется (зависит от настроек компилятора, поэтому не гарантирую). Но вы наверняка увидите предупреждение. Вот почему: в этом фрагменте происходит возврат адреса локальной переменной, которая будет уничтожена после выхода из функции.
Но ведь мы до этого спокойно вернули объект из func1()
, почему же там все было в порядке? Причина в области видимости переменной.
Оператор new
выделяет память для переменных в куче. Под кучей понимают общую в рамках процесса память. С ней ничего не случится, если только не освободить выделенную для переменной память с помощью delete
.
Память для локальных переменных выделяется в стеке. Стек существенно меньше кучи, поэтому локальные переменные в нем надолго не задерживаются.
В большинстве случаев правило определения времени жизни и области видимости локальных переменных довольно простое: локальная переменная существует только внутри фигурных скобок:
void func() {
int x = 0;
{
// Здесь x видна
int y = x;
}
// x все еще видна
// А вот y уже вышла из области видимости
// x = y; - ОШИБКА!
}
// Вне функции мы не можем использовать ни x, ни y
То, что происходит при вызове func3()
, равносильно следующему:
MyClass* pC = nullptr; // nullptr - ничто
{
MyClass c;
pC = &c;
}
// Объект c уже уничтожен, поэтому pC указывает на адрес, который БЫЛ выделен для c, но в этом месте уже недействителен
// *pC; - ОШИБКА! Код может сработать, но надеяться на это не следует
В случае же использования func1()
все нормально (похоже, но не равносильно):
MyClass c1;
{
MyClass c2;
c1 = c2;
}
// Здесь с2 уже не существует, но мы успели сделать копию, поэтому содержимое теперь в c1
Если требуется вернуть из функции переменную примитивного типа, то используйте обычное копирование (как в func1()
). Объекты создавайте с помощью new
и возвращайте через указатель (как в func2()
).
Будьте осторожны при работе с указателями! Если указатель указывает на локальную переменную, которая уже вышла из области видимости, то он становится некорректным. Пользоваться им нельзя, но компилятор вам об этом не скажет.
спасибо за статью
Anonymous:
спасибо за статью
Пожалуйста :)
)
Anonymous
Очень интересно