c ++: когда использовать указатели?

Прочитав несколько руководств, я пришел к выводу, что всегда следует использовать указатели для объектов. Но я также видел несколько исключений при чтении некоторых руководств по QT (http://zetcode.com/gui/qt4/painting/), где в стеке создается объект QPaint. Так что теперь я запутался. Когда мне следует использовать указатели?


person Jamol    schedule 18.03.2009    source источник
comment
Учебники QT создают объект QPaint в стеке, потому что срок полезного использования объекта QPaint известен во время компиляции и точно ограничен совпадающей парой {}.   -  person Thomas L Holaday    schedule 19.03.2009
comment
То же самое и для QPen, QPainter и QApplication.   -  person Thomas L Holaday    schedule 19.03.2009
comment
Что здорово знать об указателе, так это то, что теперь у нас есть в C ++ некоторая безопасная реализация указателей, таких как unique_ptr или weak_ptr, когда вам нужно использовать указатель, вам нужно использовать их (с некоторыми редкими исключениями для кода низкого уровня)   -  person MokaT    schedule 20.07.2015


Ответы (14)


Если вы не знаете, когда следует использовать указатели, просто не используйте их.

Когда вам понадобится их использовать, станет очевидно, что каждая ситуация индивидуальна. Трудно кратко резюмировать, когда их следует использовать. Не входите в привычку «всегда использовать указатели для объектов», это, безусловно, плохой совет.

person CiscoIPPhone    schedule 18.03.2009
comment
Это не ответ. Цель этого сайта - помочь людям узнать больше, а не отвергать их, если вы не можете сформулировать свои мысли. - person A.R.; 13.03.2014
comment
@ A.R. Надеюсь, я позволил джамолхону научиться не всегда использовать указатели - посмотрите на первое предложение его вопроса. Я принял во внимание этот контекст, а не только название. Кроме того, в других вопросах перечислены некоторые варианты использования. - person CiscoIPPhone; 14.03.2014
comment
Я учел последнее предложение, вы знаете, вопросную часть. - person A.R.; 14.03.2014
comment
@ A.R. Я тоже. Но учет контекста может помочь вам дать ответ, который будет более полезным, чем просто просмотр предложения, содержащего вопрос, особенно если другие люди уже ответили на него. - person CiscoIPPhone; 14.03.2014
comment
Да, я многому научился из вашего очень полезного ответа. Теперь я эксперт, когда и когда не использовать указатели. Мне особенно нравится, как вы перекрестно ссылаетесь на свои знания по теме указателей с другими людьми, которые уже обратились к этому, предоставив ссылки. Удивительный! - person A.R.; 14.03.2014
comment
@ A.R. Не нужно лукавить. Другие люди посчитали мой ответ полезным, если вы этого не сделаете, ничего страшного, просто проголосуйте против и двигайтесь дальше. - person CiscoIPPhone; 14.03.2014
comment
Кто такой язвительный? Этот ответ настолько полезен, что я действительно могу применить его к любому вопросу, который у меня когда-либо возникнет о чем-либо: если я не знаю, как его использовать, я не должен его использовать, а затем, не используя его или зная, как использовать он станет очевидным, когда и как его следует использовать! ЧИСТЫЙ ГЕНИЙ! Мое единственное желание - проголосовать ДВАЖДЫ! - person A.R.; 14.03.2014
comment
@ A.R. OP пришел к выводу, что он всегда должен использовать указатели, я считаю, что мой ответ полезен для этого конкретного случая. Я видел это много раз, даже в профессиональных базах кода, где люди используют указатели, когда в этом нет необходимости. Мой совет - не используйте их всегда, и в тех случаях, когда они вам действительно нужны, это станет очевидным. Его нельзя применять ко всем вопросам - возможно, только к тем, по которым люди пришли к выводу, что всегда следует использовать что-то, что действительно может быть вредным. - person CiscoIPPhone; 14.03.2014

Основные причины использования указателей:

  1. время жизни объекта управления;
  2. нельзя использовать ссылки (например, вы хотите сохранить в векторе что-то не копируемое);
  3. вы должны передать указатель на какую-то стороннюю функцию;
  4. возможно какие-то причины оптимизации, но я не уверен.
person bayda    schedule 18.03.2009
comment
Я думаю, что самый важный момент указателей - это контроль времени жизни объектов. - person Edison Gustavo Muenz; 18.03.2009
comment
Пункт № 2 особенно применим к Qt, так как большинство объектов, связанных с графикой Qt, не подлежат копированию. - person Arnold Spence; 18.03.2009
comment
@caparcode, Собственно, QObjects не копируются. Это включает в себя элементы графического интерфейса и очень мало графических классов, а также некоторые другие вещи. Другие классы также могут быть не копируемыми по какой-либо причине. - person strager; 19.03.2009
comment
Ссылки могут быть сохранены в векторе: std :: vector ‹boost :: optional‹ T & ›› - person Vadim Ferderer; 10.04.2009

Мне не ясно, является ли ваш вопрос ptr-to-obj vs stack-based-obj или ptr-to-obj vs reference-to-obj. Есть также варианты использования, которые не попадают ни в одну из категорий.

Что касается vs stack, похоже, это уже было рассмотрено выше. Несколько причин, наиболее очевидная - время жизни объекта.

Что касается ссылок vs, всегда старайтесь использовать ссылки, но есть вещи, которые вы можете делать, например, только с помощью ptrs (есть много вариантов использования):

  • переход по элементам в массиве (например, переход по стандартному массиву [])
  • когда вызываемая функция выделяет что-то и возвращает это через ptr

Что наиболее важно, указатели (и ссылки, в отличие от автоматических / основанных на стеке и статических объектов) поддерживают полиморфизм. Указатель на базовый класс может фактически указывать на производный класс. Это фундаментально для объектно-ориентированного поведения, поддерживаемого в C ++.

person Dan    schedule 18.03.2009
comment
+1 за последний абзац, выделенный жирным шрифтом, потому что я сам не могу проголосовать за него больше. - person David Thornley; 18.03.2009
comment
Но вы можете выделить что-то в стеке, а затем использовать это позже полиморфно, передав ссылку или указатель на вашу функцию / контейнер / что угодно. Продолжительность хранения и полиморфизм являются ортогональными понятиями. Тем не менее, люди продолжают отвечать на вопросы о том, почему можно выделять с помощью указателя, ссылаясь на полиморфизм как на преимущество, не объясняя, почему они связаны (а это не так). - person underscore_d; 09.07.2016

Во-первых, вопрос неправильный: дилемма не между указателями и стеком, а между кучей и стеком. Вы можете иметь объект в стеке и передавать указатель на этот объект. Я предполагаю, что вы действительно спрашиваете, следует ли объявлять указатель на класс или экземпляр класса.

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

С другой стороны, если объекты являются локальными для функции, то лучше использовать ее в стеке. Это позволяет компилятору вызывать деструктор, когда элемент управления выходит из функции.

person ajanicij    schedule 18.03.2009

Какие это были бы учебники? На самом деле, правило состоит в том, что вы должны использовать указатели только тогда, когда вам это абсолютно необходимо, что бывает довольно редко. Вам нужно прочитать хорошую книгу по C ++, например Accelerated C ++ от Koenig & Moo.

Изменить: чтобы немного уточнить - два случая, когда вы не использовали бы указатель (строка используется здесь как образец - то же самое относится к любому другому типу):

class Person {
   public:
      string name;       // NOT string * name;
   ...
};


void f() {
   string value;        // NOT string * value
   // use vvalue
}
person Community    schedule 18.03.2009
comment
Как насчет ограничений стека? - person Jamol; 18.03.2009
comment
какое это было бы ограничение? - person ; 18.03.2009
comment
ну, я имею в виду, что в некоторых системах, если не весь размер стека ограничен, не так ли? - person Jamol; 18.03.2009
comment
@presario: размер стека ограничен, но по обычным причинам вам не нужно использовать указатели для этого случая - person Ahmed Said; 18.03.2009
comment
Указатели - это инструмент. Ограничить их использование только в случае крайней необходимости - это слишком строго. При правильном проектировании они могут стать ключом к очень удобной и надежной конструкции. - person Nate; 18.03.2009
comment
@nate никто не предлагает обратного - person ; 18.03.2009
comment
Я не думаю, что за 15 лет у меня когда-либо закончился стек, что не было чем-то вроде глупой ошибки рекурсии. - person Rob K; 19.03.2009

Обычно указатели используются в следующих случаях:

  1. Вам понадобится набор объектов, принадлежащих к разным классам (в большинстве случаев у них будет общая база).

  2. Вам нужна коллекция объектов, размещенная в стеке, настолько большая, что это может вызвать переполнение стека.

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

  4. Вам нужна сложная логика управления временем жизни вашего объекта.

  5. Вам нужна структура данных, которая позволяет осуществлять прямую навигацию от объекта к объекту - например, связанный список, дерево или любой другой граф.

person sharptooth    schedule 18.03.2009
comment
В случае (1), возможно, нет необходимости в общей базе. В случае (2) единственная коллекция, где это применимо, будет массивом в стиле C. - person ; 18.03.2009
comment
Для случая (1) в этом нет необходимости, вы можете захотеть сохранить массив void *, чтобы иметь возможность просто вызывать free () - это не способ делать что-то в C ++, но может быть. В случае (2) я имел в виду массив указателей, а не массив реальных объектов. Если объекты действительно большие, указатели сохраняют много места в стеке. - person sharptooth; 19.03.2009

В дополнение к пунктам, которые делают другие (особенно с контролем времени жизни объекта), если вам нужно обрабатывать NULL-объекты, вы должны использовать указатели, а не ссылки. Можно создать ссылку NULL посредством приведения типов, но, как правило, это плохая идея.

person Mr Fooz    schedule 18.03.2009
comment
На самом деле это довольно ужасная идея. - person Dave Van den Eynde; 18.03.2009

Обычно используйте указатели / ссылки на объекты, когда:

  • передача их другим методам

  • создание большого массива (я не уверен, каков нормальный размер стека)

Используйте стек, когда:

  • Вы создаете объект, который живет и умирает внутри метода

  • Объект размером с регистр ЦП или меньше

person LegendLength    schedule 18.03.2009
comment
Итак, вы оба: передаете объекты в стек, создаете большие массивы в стеке, выделяете локальные переменные в куче, выделяете целые числа в куче? - person LegendLength; 19.03.2009

Я действительно использую указатели в этой ситуации:

class Foo
{
    Bar* bar;

    Foo(Bar& bar) : bar(&bar) { }

    Bar& Bar() const { return *bar; }
};

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

Дэйв

person Dave Van den Eynde    schedule 18.03.2009

Использование указателей связано с двумя ортогональными вещами:

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

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

    • some already existing interface requires that.
    • ассоциация, которая может быть нулевой, должна быть смоделирована.
    • следует избегать копирования больших объектов или копирование вообще невозможно, но нельзя использовать ссылку (например, коллекции stl).

# 1 и # 2 могут встречаться в разных конфигурациях, например, вы можете представить себе динамически выделенный объект, доступ к которому осуществляется по указателю, но такой объект также может быть передан по ссылке какой-либо функции. Вы также можете получить указатель на какой-либо объект, который создается в стеке и т. Д.

person oo_olo_oo    schedule 28.03.2009
comment
две ортогональные вещи - наконец-то это кто-то сказал! cf Я бы добавил к пункту 2, что доступ по ссылке / указателю (= адрес) включает полиморфизм, независимо от того, как был выделен объект - вопреки непонятному количеству сообщений, которые я вижу, подразумевая, что действительной причиной для динамического выделения является предоставление полиморфного поведения, которое не требуется, а не аргумент , и совершенно не связаны. - person underscore_d; 09.07.2016

Передача по значению с хорошо управляемыми копируемыми объектами - это путь для большого объема вашего кода.

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

person dicroce    schedule 28.03.2009

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

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

Если вам необходимо использовать указатели, рассмотрите некоторые классы интеллектуальных указателей, такие как auto_ptr. Автоматическое уничтожение объектов - это больше, чем просто освобождение основной памяти. Есть концепция под названием RAII. Некоторые объекты требуют дополнительной сдачи при уничтожении. например мьютексы и закрывающие файлы и т. д.

person Matt    schedule 03.03.2011
comment
Что ж, в Java нет указателей, потому что все, кроме примитива, неявно является указателем. - person mk12; 19.08.2013

Используйте указатели, если вы не хотите, чтобы ваш объект был уничтожен при опустошении фрейма стека.

По возможности используйте ссылки для передачи параметров.

person Mark Ingram    schedule 18.03.2009
comment
int x; int * pointer_to_x = Вы повторяете путаницу OP между указателями и распределением кучи. - person Pete Kirkham; 18.03.2009
comment
Нет, передача по ссылке const вместо значения - хорошая привычка. Хотя он использует указатели на уровне реализации, концептуально он не имеет к ним никакого отношения. - person David Thornley; 18.03.2009

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

Говоря конкретно о Qt, Qt помогает программисту, обрабатывая большую часть управления памятью объектов кучи. Для объектов, которые являются производными от QObject (почти все классы с префиксом «Q»), конструкторы принимают необязательный параметр parent. Затем родительский объект становится владельцем объекта, и когда родительский элемент удаляется, все принадлежащие ему объекты также удаляются. По сути, ответственность за уничтожение детей передается родительскому объекту. При использовании этого механизма дочерние QObject должны быть созданы в куче.

Короче говоря, в Qt вы можете легко создавать объекты в куче, и пока вы устанавливаете правильный родительский объект, вам нужно будет только беспокоиться об его уничтожении. Однако в целом C ++ вам нужно не забывать уничтожать объекты кучи или использовать интеллектуальные указатели.

person swongu    schedule 10.04.2009