"get () const" против "getAsConst () const" - PullRequest
1 голос
/ 19 сентября 2008

Кто-то рассказал мне о разнице в стиле 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 имеет один главный технический недостаток (подсказка: в общем коде).

Ответы [ 11 ]

10 голосов
/ 19 сентября 2008

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

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

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

8 голосов
/ 19 сентября 2008

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

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

4 голосов
/ 19 сентября 2008

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

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

1 голос
/ 19 сентября 2008

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

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

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

1 голос
/ 19 сентября 2008

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

0 голосов
/ 19 сентября 2008

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

Допустим, T является упорядоченным объектом (т. Е. Мы можем сравнивать объекты типа T с оператором

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

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 ... И, таким образом, проблема распространялась бы по всему коду ...

: - р

0 голосов
/ 19 сентября 2008

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

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

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

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

0 голосов
/ 19 сентября 2008

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

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

0 голосов
/ 19 сентября 2008

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

0 голосов
/ 19 сентября 2008

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

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