"get () const" против "getAsConst () const"

Кто-то рассказал мне о различиях в стиле C++ в их команде. У меня есть своя точка зрения по этому поводу, но мне было бы интересно услышать плюсы и минусы, исходящие от всех.

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

class T ;

class MethodA
{
   public :
      const T & get() const ;
      T & get() ;

      // etc.
} ;

class MethodB
{
   public :
      const T & getAsConst() const ;
      T & get() ;

      // etc.
} ;

Каковы будут плюсы и минусы каждого метода?

Меня больше интересуют технические / семантические причины C++, но также приветствуются соображения стиля.

Обратите внимание, что MethodB имеет один серьезный технический недостаток (подсказка: в общем коде).

Думаю, в примере есть опечатка?

Jason Dagit 19.09.2008 01:20

Это псевдокод C++. Важно то, что метод получения только для чтения в MethodA называется «get», а метод получения для чтения / записи в MethodB называется «getAsConst». Тот факт, что T не определен, является вторичным для этого обсуждения. Тем не менее, ты прав. Я исправлю это, и пропавшее "паблик" ... :-)

paercebal 19.09.2008 01:23

Ах, я думал, что увидел опечатку, что MethodA и MethodB идентичны до наименования первого метода. Учитывая формулировку вопроса, я ожидал, что один MethodB не будет иметь "T & get ();" как метод.

Jason Dagit 19.09.2008 01:36
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
2 038
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 11

Хотя, похоже, ваш вопрос касается только одного метода, я был бы рад внести свой вклад в стиль. Лично я по стилю предпочитаю первое. Большинство IDE покажут вам сигнатуру типа функций.

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

Метод Б, на мой взгляд, является случаем нарушения OnceAndOnlyOnce. И теперь вам нужно выяснить, имеете ли вы дело с константной ссылкой для написания кода, который компилируется в первый раз.

Я предполагаю, что это стилистическая вещь - технически они оба работают, но MethodA заставляет компилятор работать немного сложнее. Для меня это хорошо.

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

Лично я предпочитаю первый метод, потому что он обеспечивает более согласованный интерфейс. Кроме того, для меня getAsConst () звучит так же глупо, как и getAsInt ().

С другой стороны, вам действительно стоит дважды подумать, прежде чем возвращать неконстантную ссылку или неконстантный указатель на член данных вашего класса. Это приглашение к использованию внутренней работы вашего класса, которая в идеале должна быть скрыта. Другими словами, это нарушает инкапсуляцию. Я бы использовал get () const и set () и возвращал неконстантную ссылку, только если нет другого пути или когда это действительно имеет смысл, например, чтобы предоставить доступ для чтения / записи к элементу массива или матрица.

Вы правы насчет инкапсуляции. В этом случае «нарушение инкапсуляции» было правильным, но это выходит за рамки данного обсуждения. :-)

paercebal 19.09.2008 01:27

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

Michael Burr 19.09.2008 01:28

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

Dima 19.09.2008 01:29

Под «неконстантным геттером» я имел в виду геттер, который возвращает что-то, что не является константой. Он не должен возвращать ссылку на что-то внутри объекта. Вы правы, независимо от того, что геттер должен быть const (в общем).

Michael Burr 19.09.2008 09:17

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

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

Ну, во-первых, getAsConst должен будет вызываться, когда указатель this является константным, а не когда вы хотите получить константный объект. Так что, наряду с любыми другими проблемами, он назван неправильно. (Вы все еще можете называть это, когда 'this' не является константой, но это ни здесь, ни там.)

Игнорируя это, getAsConst ничего не приносит и ложится чрезмерным бременем на разработчика, использующего интерфейс. Вместо того, чтобы просто вызвать get и знать, что он получает то, что ему нужно, теперь он должен выяснить, использует ли он в настоящее время переменную const, и должен ли новый объект, который он захватывает, быть const. А позже, если оба объекта станут неконстантными из-за некоторого рефакторинга, ему придется отключить свой вызов.

Учитывая прецедент стиля, установленный стандартной библиотекой (то есть begin () и begin () const, чтобы назвать только один пример), должно быть очевидно, что метод A является правильным выбором. Я сомневаюсь в здравомыслии человека, который выбирает метод Б.

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

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

Поскольку вы скрываете имена классов, эта пища для размышлений о стиле может применяться, а может и не применяться:

Имеет ли смысл указывать этим двум объектам, MethodA и MethodB, «get» или «getAsConst»? Вы бы отправили «get» или «getAsConst» в виде сообщений любому объекту?

На мой взгляд, как отправитель сообщения / инициатора метода ты получает значение; поэтому в ответ на это сообщение "get" вы отправляете какое-то сообщение в MethodA / MethodB, результатом которого является значение, которое вам нужно получить.

Пример: если вызывающий MethodA - это, скажем, сервис в SOA, а MethodA - репозиторий, то внутри get_A () сервиса вызовите MethodA.find_A_by_criteria (...).

Я сбит с толку ... Полагаю, вы упустили суть вопроса. Этот вопрос очень специфичен для C++ и его нелегко перенести на другие языки (AFAIK, ни в C#, ни в Java, ни в JavaScript, Basic и т. д.). Речь идет о константе и ее использовании в C++.

paercebal 19.09.2008 02:16

Возможно Вы правы. Полагаю, с моей точки зрения, единственное отличие, которое выскочило, - это названия методов.

moffdub 19.09.2008 03:59

Итак, первый стиль обычно предпочтительнее.

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

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

Главный технологический недостаток MethodB, который я видел, заключается в том, что при применении к нему универсального кода мы должны удвоить код для обработки как константной, так и неконстантной версии. Например:

Скажем, T - это упорядочиваемый объект (то есть мы можем сравнивать с объектами типа T с помощью operator <), и допустим, мы хотим найти максимум между двумя MethodA (соответственно, двумя MethodB).

Для MethodA все, что нам нужно кодировать, это:

template <typename T>
T & getMax(T & p_oLeft, T & p_oRight)
{
   if (p_oLeft.get() > p_oRight.get())
   {
      return p_oLeft ;
   }
   else
   {
      return p_oRight ;
   }
}

Этот код будет работать как с константными объектами, так и с неконстантными объектами типа T:

// Ok
const MethodA oA_C0(), oA_C1() ;
const MethodA & oA_CResult = getMax(oA_C0, oA_C1) ;

// Ok again
MethodA oA_0(), oA_1() ;
MethodA & oA_Result = getMax(oA_0, oA_1) ;

Проблема возникает, когда мы хотим применить этот простой код к чему-то в соответствии с соглашением MethodB:

// NOT Ok
const MethodB oB_C0(), oB_C1() ;
const MethodB & oB_CResult = getMax(oB_C0, oB_C1) ; // Won't compile

// Ok
MethodA oB_0(), oB_1() ;
MethodA & oB_Result = getMax(oB_0, oB_1) ;

Чтобы MethodB работал как с константной, так и с неконстантной версией, мы оба должны использовать уже определенный getMax, но добавить к нему следующую версию getMax:

template <typename T>
const T & getMax(const T & p_oLeft, const T & p_oRight)
{
   if (p_oLeft.getAsConst() > p_oRight.getAsConst())
   {
      return p_oLeft ;
   }
   else
   {
      return p_oRight ;
   }
}

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

Конечно, при достаточной паранойе вторая функция-шаблон должна была называться getMaxAsConst ... И, таким образом, проблема распространялась бы на весь код ...

:-п

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