Каковы некоторые недостатки использования ссылки вместо указателя?

#c #pointers #coding-style #reference

#c #указатели #стиль кодирования #ссылка

Вопрос:

Учитывая, что класс «А» существует и является правильным. Каковы были бы некоторые негативные результаты использования ссылки на «A» вместо указателя в классе «B». Это:

 // In Declaration File
class A;

class B
{
public:
   B();
   ~B();
private:
    Aamp; a;
};

// In Definition File
B::B(): a(* new A())
{}

B::~B()
{
    delete amp;a;
}
  

Опустил дополнительный код для дальнейшей корректности «B», такой как конструктор копирования и оператор присваивания, просто хотел продемонстрировать концепцию вопроса.

Комментарии:

1. Интересно. Я раньше не видел такого кода. Может быть, это просто способ избежать разыменования и использования, по какой-то причине -> все еще делая a динамически выделяемым 😕 1 за вопрос.

2. В чем смысл этого, а не просто A a; ? Разве это не было бы лучше, чем обе ссылки или указатели?

3. @tenfour во многих случаях, но это упрощенный пример — предположим, что значение за a создается фабрикой.

4. @tenfour Также использование объекта value предотвращает прямые объявления.

5. Инициализация ссылки разыменованным указателем, полученным с помощью new , не является идиоматической для C .

Ответ №1:

Непосредственные ограничения заключаются в том, что:

  • Вы не можете изменить значение ссылки. Вы можете изменить то, на A что оно ссылается, но вы не можете перераспределить или переназначить a в течение B срока службы.
  • a никогда не должно быть 0 .

Таким образом:

  • Объект не может быть присвоен.
  • B не должен поддаваться копированию, если только вы не научите A и его подтипы правильно клонировать.
  • B не будет хорошим кандидатом в качестве элемента типов коллекций, если хранится как значение. Вектор B s, вероятно, будет проще всего реализован как std::vector<B*> , что может привести к дальнейшим осложнениям (или упрощениям, в зависимости от вашего дизайна).

Это может быть полезно, в зависимости от ваших потребностей.

Предостережения:

  • нарезка — это еще одна проблема, о которой следует знать, если a она присваивается и присвоение достижимо внутри B .

Комментарии:

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

2. Отредактировано, чтобы отразить, что отсутствующим кодом были конструктор копирования и оператор присваивания.

3. Этот первый «недостаток» — очень, очень хорошая вещь , а второй просто расширяет его. То, что B это больше не будет присваиваемым, тоже хорошо, потому что это лучше, чем B то, что это можно присваивать нерегулярным способом.

4. @timday: Это также останавливает вас от внедрения ошибок во время жизни динамического объекта… в некоторой степени.

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

Ответ №2:

Вы не можете изменить объект, на который ссылается a, постфактум, например, при присвоении. Кроме того, это делает ваш тип не-POD (указанный тип в любом случае был бы не-POD из-за элемента private data, но в некоторых случаях это может иметь значение).

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

Ответ №3:

Конечно, добавление ссылочного элемента в ваш класс B означает, что компилятор больше не может генерировать неявные конструкторы по умолчанию и копирования и операторы присваивания; и ни написанный вручную оператор присваивания не может переназначаться a .

Я не думаю, что есть отрицательные результаты, кроме того факта, что delete amp;a это может выглядеть странно. Тот факт, что объект был создан с помощью new , несколько теряется при привязке результата к ссылке, и это может иметь значение только потому, что тот факт, что его время жизни должно контролироваться B , неясен.

Ответ №4:

Если вы используете ссылку:

  • Вы должны указать значение во время построения
  • Вы не можете изменить, на что это ссылается
  • Это не может быть null
  • Это предотвратит возможность назначения вашего класса

Возможно, вы могли бы рассмотреть возможность использования какого-либо интеллектуального указателя вместо этого (std::unique_ptr, std::shared_ptr и т.д.). Это имеет дополнительное преимущество автоматического удаления объекта для вас.

Ответ №5:

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

Ответ №6:

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