Прочитав несколько руководств, я пришел к выводу, что всегда следует использовать указатели для объектов. Но я также видел несколько исключений при чтении некоторых руководств по QT (http://zetcode.com/gui/qt4/painting/), где в стеке создается объект QPaint. Так что теперь я запутался. Когда мне следует использовать указатели?
c ++: когда использовать указатели?
Ответы (14)
Если вы не знаете, когда следует использовать указатели, просто не используйте их.
Когда вам понадобится их использовать, станет очевидно, что каждая ситуация индивидуальна. Трудно кратко резюмировать, когда их следует использовать. Не входите в привычку «всегда использовать указатели для объектов», это, безусловно, плохой совет.
Основные причины использования указателей:
- время жизни объекта управления;
- нельзя использовать ссылки (например, вы хотите сохранить в векторе что-то не копируемое);
- вы должны передать указатель на какую-то стороннюю функцию;
- возможно какие-то причины оптимизации, но я не уверен.
Мне не ясно, является ли ваш вопрос ptr-to-obj vs stack-based-obj или ptr-to-obj vs reference-to-obj. Есть также варианты использования, которые не попадают ни в одну из категорий.
Что касается vs stack, похоже, это уже было рассмотрено выше. Несколько причин, наиболее очевидная - время жизни объекта.
Что касается ссылок vs, всегда старайтесь использовать ссылки, но есть вещи, которые вы можете делать, например, только с помощью ptrs (есть много вариантов использования):
- переход по элементам в массиве (например, переход по стандартному массиву [])
- когда вызываемая функция выделяет что-то и возвращает это через ptr
Что наиболее важно, указатели (и ссылки, в отличие от автоматических / основанных на стеке и статических объектов) поддерживают полиморфизм. Указатель на базовый класс может фактически указывать на производный класс. Это фундаментально для объектно-ориентированного поведения, поддерживаемого в C ++.
Во-первых, вопрос неправильный: дилемма не между указателями и стеком, а между кучей и стеком. Вы можете иметь объект в стеке и передавать указатель на этот объект. Я предполагаю, что вы действительно спрашиваете, следует ли объявлять указатель на класс или экземпляр класса.
Ответ в том, что это зависит от того, что вы хотите делать с объектом. Если объект должен существовать после того, как элемент управления покидает функцию, вы должны использовать указатель и создать объект в куче. Вы сделаете это, например, когда ваша функция должна вернуть указатель на созданный объект или добавить объект в список, который был создан перед вызовом вашей функции.
С другой стороны, если объекты являются локальными для функции, то лучше использовать ее в стеке. Это позволяет компилятору вызывать деструктор, когда элемент управления выходит из функции.
Какие это были бы учебники? На самом деле, правило состоит в том, что вы должны использовать указатели только тогда, когда вам это абсолютно необходимо, что бывает довольно редко. Вам нужно прочитать хорошую книгу по C ++, например Accelerated C ++ от Koenig & Moo.
Изменить: чтобы немного уточнить - два случая, когда вы не использовали бы указатель (строка используется здесь как образец - то же самое относится к любому другому типу):
class Person {
public:
string name; // NOT string * name;
...
};
void f() {
string value; // NOT string * value
// use vvalue
}
Обычно указатели используются в следующих случаях:
Вам понадобится набор объектов, принадлежащих к разным классам (в большинстве случаев у них будет общая база).
Вам нужна коллекция объектов, размещенная в стеке, настолько большая, что это может вызвать переполнение стека.
Вам нужна структура данных, которая может быстро переупорядочивать объекты - например, связанный список, дерево и тому подобное.
Вам нужна сложная логика управления временем жизни вашего объекта.
Вам нужна структура данных, которая позволяет осуществлять прямую навигацию от объекта к объекту - например, связанный список, дерево или любой другой граф.
В дополнение к пунктам, которые делают другие (особенно с контролем времени жизни объекта), если вам нужно обрабатывать NULL-объекты, вы должны использовать указатели, а не ссылки. Можно создать ссылку NULL посредством приведения типов, но, как правило, это плохая идея.
Обычно используйте указатели / ссылки на объекты, когда:
передача их другим методам
создание большого массива (я не уверен, каков нормальный размер стека)
Используйте стек, когда:
Вы создаете объект, который живет и умирает внутри метода
Объект размером с регистр ЦП или меньше
Я действительно использую указатели в этой ситуации:
class Foo
{
Bar* bar;
Foo(Bar& bar) : bar(&bar) { }
Bar& Bar() const { return *bar; }
};
До этого я использовал ссылочные элементы, инициализированные из конструктора, но у компилятора возникла проблема с созданием конструкторов копирования, операторов присваивания и всего пакета.
Дэйв
Использование указателей связано с двумя ортогональными вещами:
Динамическое размещение. В общем, вы должны выделять динамически, когда объект должен жить дольше, чем область, в которой он был создан. Такой объект представляет собой ресурс, владелец которого должен быть четко указан (чаще всего это своего рода интеллектуальный указатель).
Доступ по адресу (независимо от того, как был создан объект). В этом контексте указатель не означает владение. Такой доступ может понадобиться, когда:
- some already existing interface requires that.
- ассоциация, которая может быть нулевой, должна быть смоделирована.
- следует избегать копирования больших объектов или копирование вообще невозможно, но нельзя использовать ссылку (например, коллекции stl).
# 1 и # 2 могут встречаться в разных конфигурациях, например, вы можете представить себе динамически выделенный объект, доступ к которому осуществляется по указателю, но такой объект также может быть передан по ссылке какой-либо функции. Вы также можете получить указатель на какой-либо объект, который создается в стеке и т. Д.
Передача по значению с хорошо управляемыми копируемыми объектами - это путь для большого объема вашего кода.
Если скорость действительно имеет значение, используйте передачу по ссылке там, где это возможно, и, наконец, используйте указатели.
По возможности никогда не используйте указатели. Положитесь на передачу по ссылке или, если вы собираетесь вернуть структуру или класс, предположите, что ваш компилятор имеет оптимизацию возвращаемого значения. (Однако вам следует избегать условного построения возвращаемого класса).
Есть причина, по которой в Java нет указателей. С ++ они тоже не нужны. Если вы избегаете их использования, вы получите дополнительное преимущество автоматического уничтожения объекта, когда объект покидает область видимости. В противном случае ваш код будет генерировать ошибки памяти различных типов. Утечки памяти бывает очень сложно найти, и они часто возникают в C ++ из-за необработанных исключений.
Если вам необходимо использовать указатели, рассмотрите некоторые классы интеллектуальных указателей, такие как auto_ptr. Автоматическое уничтожение объектов - это больше, чем просто освобождение основной памяти. Есть концепция под названием RAII. Некоторые объекты требуют дополнительной сдачи при уничтожении. например мьютексы и закрывающие файлы и т. д.
Используйте указатели, если вы не хотите, чтобы ваш объект был уничтожен при опустошении фрейма стека.
По возможности используйте ссылки для передачи параметров.
Говоря о C ++, объекты, созданные в стеке, нельзя использовать, когда программа вышла из области видимости, в которой она была создана. В общем, когда вы знаете, что вам не нужна переменная после функции или закрытой скобки, вы можете ее создать. в стеке.
Говоря конкретно о Qt, Qt помогает программисту, обрабатывая большую часть управления памятью объектов кучи. Для объектов, которые являются производными от QObject (почти все классы с префиксом «Q»), конструкторы принимают необязательный параметр parent. Затем родительский объект становится владельцем объекта, и когда родительский элемент удаляется, все принадлежащие ему объекты также удаляются. По сути, ответственность за уничтожение детей передается родительскому объекту. При использовании этого механизма дочерние QObject должны быть созданы в куче.
Короче говоря, в Qt вы можете легко создавать объекты в куче, и пока вы устанавливаете правильный родительский объект, вам нужно будет только беспокоиться об его уничтожении. Однако в целом C ++ вам нужно не забывать уничтожать объекты кучи или использовать интеллектуальные указатели.