Должен ли я предпочитать указатели или ссылки в данных члена?

Это упрощенный пример, иллюстрирующий вопрос:

class A {};

class B
{
    B(A& a) : a(a) {}
    A& a;
};

class C
{
    C() : b(a) {} 
    A a;
    B b; 
};

Итак, B отвечает за обновление части C. Я запускал код через lint, и он болтал о ссылочном элементе: lint # 1725, Это говорит о том, что нужно тщательно следить за копиями и присваиваниями по умолчанию, но по умолчанию копия и присваивание также плохо связаны с указателями, поэтому там мало преимуществ.

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

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

Можно ли сказать, что объект, содержащий ссылку, нельзя присваивать, так как ссылка не должна быть изменена после инициализации?

Ответ 1

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

Примеры проблем:

  • вы вынуждены инициализировать ссылку в каждом списке инициализаторов конструктора: нет способа отделить эту инициализацию от другой функции (до тех пор, пока C++ 0x, в любом случае отредактируйте: C++ теперь имеет делегирование конструкторов)
  • ссылка не может быть отскока или быть нулевой. Это может быть преимуществом, но если код когда-либо нуждается в изменении, чтобы разрешить повторное связывание или для члена был пустым, все действия члена должны измениться
  • в отличие от элементов указателя ссылки не могут быть легко заменены интеллектуальными указателями или итераторами, поскольку может потребоваться рефакторинг
  • Всякий раз, когда используется ссылка, он выглядит как тип значения (.оператор и т . Д.), Но ведет себя как указатель (может болтаться) - так, например, руководство по стилю Google отпугивает его

Ответ 2

Мое собственное правило:

  • Используйте ссылочный элемент, когда вы хотите, чтобы жизнь вашего объекта зависела от жизни других объектов.. Это явный способ сказать, что вы не позволяете объекту быть живым без действительный экземпляр другого класса - из-за отсутствия назначения и обязанности получить инициализацию ссылок через конструктор. Это хороший способ создать свой класс, не предполагая что-либо о том, что экземпляр является членом или нет другого класса. Вы только предполагаете, что их жизнь напрямую связана с другими экземплярами. Это позволяет вам позже изменить способ использования экземпляра класса (с новым, как локальным экземпляром, как членом класса, сгенерированным пулом памяти в менеджере и т.д.).
  • Использовать указатель в других случаях. Если вы хотите, чтобы член был изменен позже, используйте указатель или указатель const, чтобы читать только точный экземпляр. Если этот тип предполагается скопировать, вы все равно не можете использовать ссылки. Иногда вам также нужно инициализировать элемент после специального вызова функции (например, init()), а затем у вас просто нет выбора, кроме для использования указателя. НО: использование утверждений во всей вашей функции-члене для быстрого обнаружения неправильного состояния указателя!
  • В тех случаях, когда вы хотите, чтобы время жизни объекта зависело от времени жизни внешнего объекта, и вам также нужен этот тип для копирования, а затем используйте элементы указателя, но ссылочный аргумент в конструкторе. Таким образом, вы указывая при построении, что время жизни этого объекта зависит от времени жизни аргумента, но указатели использования реализации все еще могут быть скопированы. Пока эти члены изменяются только копией, а ваш тип не имеет конструктора по умолчанию, тип должен заполнять обе цели.

Ответ 3

Объекты редко должны позволять присваивать и другие вещи, такие как сравнение. Если вы рассматриваете некоторую бизнес-модель с такими объектами, как "Департамент", "Сотрудник", "Директор", трудно представить себе случай, когда один сотрудник будет назначен другому.

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

И, вероятно, нормально описывать одно-или-нулевое отношение как указатель.

Так что "мы не можем назначить" то коэффициент.
Многие программисты просто привыкают к указателям и почему они найдут какие-либо аргументы, чтобы избежать использования ссылки.

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

Ответ 5

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

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

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

Ответ 6

Как все, кажется, раздают общие правила, я предлагаю два:

  • Никогда, никогда не используйте ссылки использования в качестве членов класса. Я никогда не делал этого в своем собственном коде (кроме того, чтобы доказать себе, что я прав в этом правиле) и не могу представить случай, когда я это сделаю. Семантика слишком запутанна, и на самом деле это не то, на что были рассчитаны ссылки.

  • Всегда, всегда используйте ссылки при передаче параметров в функции, кроме основных типов, или когда алгоритм требует копии.

Эти правила просты, и я стоял хорошо. Я оставляю правила использования интеллектуальных указателей (но, пожалуйста, не auto_ptr) как членов класса другим.

Ответ 7

Да, чтобы: Можно ли сказать, что объект, содержащий ссылку, нельзя присваивать, так как ссылка не должна быть изменена после инициализации?

Мои эмпирические правила для членов данных:

  • никогда не используйте ссылку, потому что она предотвращает назначение
  • Если ваш класс отвечает за удаление, используйте boost scoped_ptr (что более безопасно, чем auto_ptr)
  • в противном случае используйте указатель указателя или константы

Ответ 8

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

Да. Читаемость вашего кода. Указатель делает более очевидным, что элемент является ссылкой (по иронии судьбы:)), а не содержащимся объектом, потому что, когда вы его используете, вы должны де-ссылку на него. Я знаю, что некоторые люди думают, что это старомодно, но я все же думаю, что это просто предотвращает путаницу и ошибки.

Ответ 9

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