Когда использовать указатели, а когда нет? - PullRequest
27 голосов
/ 29 декабря 2008

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

class Foo {
    Bar b;
}

В противоположность этому:

class Foo {
    Bar* b;
}

Ответы [ 5 ]

39 голосов
/ 29 декабря 2008

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

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

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

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

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

19 голосов
/ 29 декабря 2008
class Foo {
    Bar b;
}

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

class Foo {
    Bar * b;
}

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

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

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

3 голосов
/ 29 декабря 2008

В первом примере память для объекта 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.

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

1 голос
/ 29 декабря 2008

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

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

1 голос
/ 29 декабря 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...