Когда использовать указатели, а когда нет?

Я привык заниматься программированием на Java, когда при программировании никогда не приходится думать об указателях. Однако сейчас я пишу программу на C++. Когда я должен использовать указатели, а когда нет, при создании классов, которые имеют члены других классов? Например, когда я хочу это сделать:

class Foo {
    Bar b;
}

В отличие от этого:

class Foo {
    Bar* b;
}
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
28
0
7 812
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Оба подходят в разных условиях. Например, если вы знаете, как построить b, когда создается объект класса Foo, первое - в порядке. Но если вы этого не сделаете, единственный выбор - использовать второй.

Ответ принят как подходящий

Начните с избегания указателей.

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

  • Вы хотите использовать Идиома Pimpl или абстрактная фабрика.
  • Экземпляр Bar фактически управляется какой-то другой частью вашей программы, тогда как класс Foo просто должен иметь к нему доступ.
  • Вы хотите отложить создание объекта Bar (т.е. вы хотите создать его после, создавая Foo).
  • В вашей бизнес-логике объект Bar может вообще не существовать; вы бы также использовали null в Java. Однако проверьте также boost :: optional.
  • Bar на самом деле является базовым классом, и вам нужно, чтобы экземпляр был полиморфным.
  • Вы случайно используете набор инструментов, который предпочитает представлять виджеты графического интерфейса в виде указателей. Примеры могут включать (но не ограничиваются ими) wxWidgets и GLUI.

В любом из этих случаев (*) начните с использования интеллектуального указателя, такого как boost :: shared_ptr. В противном случае вы, скорее всего, рано или поздно забудете освободить память. Когда вы поймете, что делаете, в каждом конкретном случае подумайте, какой тип указателя лучше всего подходит.

(*) в любом случае - кроме, наверное, пункта о виджетах GUI; в этом случае ваш инструментарий, скорее всего, также будет управлять ресурсами за вас.

... или в случае, если вы пишете инструментарий (что часто бывает в моем случае). У меня никогда не было проблем с указателями, которые могли бы решить интеллектуальные указатели, поэтому я бы не стал говорить «ИСПОЛЬЗУЙТЕ ИХ!» но «в большинстве случаев их проще всего использовать».

strager 29.12.2008 10:45

может я один. но я думаю, что особенно новички должны (конечно, не в реальных проектах) сначала попробовать поработать с необработанными указателями. Думаю, это похоже на вопрос «IDE / no IDE». они должны знать, что умные указатели Зачем полезны и каких ловушек они избегают

Johannes Schaub - litb 29.12.2008 11:51

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

Johannes Schaub - litb 29.12.2008 11:51

@litb: Я согласен с вашей точкой зрения. Думаю, ответ будет немного другим, если вы спросите о лучших практиках для реальных проектов (на этот вопрос я отвечал) или научитесь понимать фундаментальные вещи. OTOH, я подумал, что он уже знал, как работать с необработанными указателями в базовом случае.

Reunanen 29.12.2008 12:26

Начальная точка должна быть boost :: scoped_ptr, используйте shared_ptr только тогда, когда вам нужна эта функциональность.

Patrick 29.12.2008 12:38

@ Патрик: Верно. «Поскольку он не копируемый, он безопаснее shared_ptr или std :: auto_ptr для указателей, которые не следует копировать». Однако это не сработает в ситуации, описанной во втором пункте.

Reunanen 29.12.2008 12:54

Если вы программируете на Qt, используйте QPointer <T> как умный указатель.

Nathan Moos 10.04.2011 02:25

В первом примере память для объекта Bar будет выделена автоматически при создании объекта Foo. Во втором случае вам нужно выделить память самостоятельно. Итак, вы должны вызвать:

Foo *foo = new Foo();
foo->b = new Bar();

Это может быть желательно, если объект Bar большой и вы не хотите связывать его с объектом Foo. Также желательно, чтобы создание объекта Bar не зависело от создания объекта Foo. В этом случае объект b "вводится" в foo:

Foo *foo = new Foo();
foo->b = b_ptr;

где b_ptr создается где-то еще, а указатель передается на foo.

Для небольших объектов это опасно, так как вы можете забыть выделить память.

new возвращает указатель, а не значение. Вы имеете в виду Foo foo; или Foo foo = Foo (); вместо?
strager 29.12.2008 10:34
class Foo {
    Bar b;
}

b - это содержал в Foo. Если у объекта Foo заканчивается время жизни, b автоматически завершает время жизни. Это то, что моделирует композиция. b выше обозначает сам объект, а не просто указатель на него, как в Java. Поэтому, если b выходит за пределы области видимости, срок жизни объекта истекает.

class Foo {
    Bar * b;
}

Здесь объект b указывает на использован или на который ссылается объект Foo. Если время жизни объекта Foo заканчивается, объект b, на который указывает, может продолжать жить, в зависимости от обстоятельств. Это можно использовать для моделирования агрегирования и общих отношений. Например, этот объект может использоваться совместно с другими объектами Foo.

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

Ссылки похожи на указатели. Ссылки в C++ должны быть инициализированы и могут указывать только на один (действительный) объект, для которого была инициализирована ссылка. Поэтому ссылка не может содержать значение, которое могло бы означать «ничего», как null в Java.

Это может быть слишком поздно, но в первом абзаце не следует ли вам говорить: «Если у объекта Foo заканчивается время жизни, b автоматически завершает время жизни». вместо Bar вместо Foo.

His 01.03.2010 05:55

Вам нужно немного заняться программированием на ассемблере и хорошо понимать структуру памяти. C - это просто кроссплатформенная сборка, в отличие от Java или других языков. Чтобы использовать его правильно, нужно понимать детали низкого уровня.

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

Обратите внимание, что вопрос касался C++, а не C.

Reunanen 27.04.2009 00:57

Но новички не знают разницы между C++ и C. Это был хороший совет.

toto 05.10.2009 06:28

Другие вопросы по теме