Это более тонко, чем предполагают другие ответы. Нет абсолютного разделения между данными в стеке и данными в куче в зависимости от того, как вы их объявляете. Например:
std::vector<int> v(10);
В теле функции, которая объявляет vector
(динамический массив) из десяти целых чисел в стеке. Но хранилище, которым управляет, vector
отсутствует в стеке.
Ах, но (другие ответы предполагают) время жизни этого хранилища ограничено временем жизни самого vector
себя, которое здесь основано на стеке, поэтому не имеет значения, как оно реализовано - мы можем рассматривать его только как объект на основе стека с семантикой значений.
Не так. Предположим, функция была:
void GetSomeNumbers(std::vector<int> &result)
{
std::vector<int> v(10);
// fill v with numbers
result.swap(v);
}
Таким образом, все, что swap
связано с функцией (а любой сложный тип значения должен иметь ее), может служить своего рода повторно привязанной ссылкой на некоторые данные кучи в системе, которая гарантирует единственного владельца этих данных.
Поэтому современный подход C ++ заключается в том, чтобы никогда не хранить адрес данных кучи в переменных голых локальных указателей. Все выделения кучи должны быть скрыты внутри классов.
Если вы это сделаете, вы можете думать обо всех переменных в своей программе, как если бы они были простыми типами значений, и вообще забыть о куче (кроме случаев написания нового класса-оболочки, подобного значению, для некоторых данных кучи, что должно быть необычным) ,
Вам просто нужно сохранить одну особую информацию, которая поможет вам оптимизировать: где это возможно, вместо того, чтобы назначать одну переменную другой, как это:
a = b;
поменяйте их местами так:
a.swap(b);
потому что это намного быстрее и не вызывает исключений. Единственное требование состоит в том, что вам не нужно b
продолжать хранить то же самое значение ( a
вместо этого оно получит значение, которое будет потеряно a = b
).
Обратной стороной является то, что этот подход заставляет вас возвращать значения из функций через выходные параметры вместо фактического возвращаемого значения. Но они исправляют это в C ++ 0x с помощью ссылок rvalue .
В самых сложных ситуациях вы довели бы эту идею до крайности и использовали бы класс интеллектуального указателя, такой как тот, shared_ptr
который уже находится в tr1. (Хотя я бы сказал, что если вам это нужно, вы, возможно, вышли за пределы сладкого места применимости Стандартного C ++.)