Чрезмерное использование this в C ++

Я имею дело с большой базой кода, в которой повсюду используется следующая конструкция

class MyClass
{
public:
  void f(int x);
private:
  int x;
};


void MyClass::f(int x)
{
'
'
  this->x = x;
'
'
}

Лично я всегда использовал и, следовательно, предпочел бы форму

class MyClass
{
public:
  void f(int x);
private:
  int _x;
};


void MyClass::f(int x)
{
'
'
  _x = x;
'
'
}

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


person SmacL    schedule 29.06.2009    source источник
comment
Ваш вопрос не имеет ничего общего с рефакторингом или кодовыми запахами. Это связано только с соглашением об именах, которому вы выбираете следовать, что, в свою очередь, заставляет вас использовать ключевое слово this, чтобы избежать двусмысленности (и не было бы необходимости, если бы параметр был назван y).   -  person Groo    schedule 29.06.2009
comment
@Groo, IMO, выбор соглашения об именах, которое может привести к двусмысленности имени переменной и, следовательно, к ошибкам - это запах, поскольку заставляют использовать 'this' для разрешения неоднозначности. Если это запах и это код, над которым я, вероятно, буду работать, это, скорее всего, приведет к рефакторингу.   -  person SmacL    schedule 29.06.2009
comment
Я предпочитаю использовать _x и x наоборот, т.е. _x является временным, просто потому, что это на одно нажатие клавиши меньше.   -  person Chris Huang-Leaver    schedule 29.06.2009
comment
Но есть единственный случай, который однозначно различает параметры, поля экземпляра и статические поля, независимо от вашего соглашения и стиля - это использование ключевого слова this (Google для StyleCop SA1309). Любой другой способ - это просто предпочтительный стиль программиста, и он может меняться от одного источника к другому. ‹Subjective› Не поймите меня неправильно - я всегда ставлю перед закрытыми полями знак подчеркивания. :) ‹/subjective›   -  person Groo    schedule 29.06.2009
comment
Не путайте больше набираемых штрихов и больше кода ... оба имеют одинаковое количество кода, следовательно, одинаковая вероятность ошибок. Разница в том, что у одного нажатия клавиш больше, чем у другого. Хотя это сводит на нет половину вашего аргумента, часть двусмысленности остается для этого соглашения, если это опущено.   -  person Newtopian    schedule 15.07.2009
comment
@Newtopian this- ›x = x - явное разыменование указателя, которое представляет собой больше кода, чем _x = x, даже если последнее включает аналогичное разыменование неявно и приводит к тому же результату после компиляции. Например, предположим, что я случайно набрал thos- ›x вместо this-› x, где thos был указателем NULL типа MyClass. Маловероятно, но все же есть возможность для новых ошибок.   -  person SmacL    schedule 16.07.2009
comment
Для учеников я всегда использую его как m_varName. Итак, если у вас есть переменная, которая равна varName в параметре, вы можете использовать ее как m_varName = varName;. Это делает его читабельным, вы видите, какие из переменных являются членами, очень помогает с автозаполнением при написании кода, и это не зарезервированное имя.   -  person JohnJohn    schedule 31.01.2015


Ответы (15)


Ваша версия немного чище, но пока вы ее, я бы:

  1. Избегайте начального подчеркивания: _x можно, пока кто-нибудь не выберет _MyField, которое является зарезервированным именем. Начальное подчеркивание, за которым следует заглавная буква, не допускается в качестве имени переменной. См .: Каковы правила использования символа подчеркивания в идентификаторе C ++?
  2. Сделайте атрибут частным или защищенным: изменение безопасно, если оно компилируется, и вы убедитесь, что ваш установщик будет использоваться.
  3. История this-> используется, например, в шаблонном коде, чтобы сделать имя поля зависимым от вашего типа (может решить некоторые проблемы с поиском).

Небольшой пример разрешений имен, которые фиксируются с помощью явного this-> (проверено с g ++ 3.4.3):

#include <iostream>
#include <ostream>

class A
{
public:
  int g_;
  A() : g_(1) {}
  const char* f() { return __FUNCTION__; }
};

const char* f() { return __FUNCTION__; }
int g_ = -1;

template < typename Base >
struct Derived : public Base
{
  void print_conflicts()
  {
    std::cout << f() << std::endl; // Calls ::f()
    std::cout << this->f() << std::endl; // Calls A::f()
    std::cout << g_ << std::endl; // Prints global g_
    std::cout << this->g_ << std::endl; // Prints A::g_
  }
};

int main(int argc, char* argv[])
{
   Derived< A >().print_conflicts();
   return EXIT_SUCCESS;
}
person bltxd    schedule 29.06.2009
comment
На самом деле большая часть кода - это MFC, а в конечном итоге m_ в соответствии с большинством MFC. Вместо этого я использовал простое подчеркивание, чтобы сделать вопрос немного более нейтральным, и точно так же не упомянул частное, поскольку это уже другая проблема. +1 за вопрос о шаблонах, возможно, вы могли бы привести небольшой пример, поскольку он действительно поможет в ответе. - person SmacL; 29.06.2009
comment
Подчеркивание в начале допустимо, если следующий символ находится в нижнем регистре. - person GManNickG; 29.06.2009
comment
Верно, но сопровождающий вас сопровождающий может не знать об этом. - person bltxd; 29.06.2009

Именование полей не имеет ничего общего с кодовым запахом. Как сказал Нил, видимость поля - единственный здесь кодовый запах.

Существуют различные статьи, касающиеся соглашений об именах в C ++:

и Т. Д.

person Groo    schedule 29.06.2009
comment
На самом деле я не спрашивал, было ли соглашение об именах запахом кода, я спрашивал, не является ли чрезмерное использование «this-› »запахом кода. Я подозреваю, что Марко (ниже) прав в том, что практика добавления «this-› »происходит из-за желания использовать Intellisense / автозавершение кода в некоторых IDE. Если это так, то, вероятно, это запах кода. - person SmacL; 29.06.2009

Такое использование слова "это" поддерживается стандартами кодирования Microsoft C #. Он дает хорошую ясность кода и призван стать стандартом по сравнению с использованием m_ или _ или чего-либо еще в переменных-членах.

Честно говоря, мне все равно не нравится подчеркивание в именах, я обычно ставил перед всеми участниками одну букву «m».

person Jem    schedule 29.06.2009
comment
Спасибо, что поделились со всеми нами своими предпочтениями по именованию переменных. - person Eric; 29.06.2009
comment
Стандарты кодирования C # :) Чтобы более подробно ответить на вопрос, я бы не стал рассматривать это как запах кода, потому что это даже считается хорошей практикой на другом языке, где, IMHO, применяются аналогичные плюсы и минусы. - person Jem; 29.06.2009
comment
@ Эрик, я хотел показать, что это дело вкуса. Подчеркивание в любом месте нестандартного библиотечного кода, как правило, снижает видимость и может рассматриваться как запах кода по сравнению с его использованием. Это, конечно, всего лишь мнение. - person Jem; 29.06.2009
comment
C # и C ++ - разные языки. В C ++ this- ›имеет очень специфическое использование для принудительного поиска идентификатора в контексте создания экземпляра шаблона в качестве члена, а не в контексте определения шаблона. Стандарт кодирования C ++ может разумно ограничить использование this- ›там, где это действительно необходимо. Это не относится к C #. - person CB Bailey; 29.06.2009
comment
Я обычно использую m_ в коде MFC и единственный символ подчеркивания в другом месте, опять же, просто личные предпочтения. - person SmacL; 29.06.2009
comment
Я никогда не видел, чтобы стандарт кодирования C # поощрял this, хотя в какой-то момент он препятствовал m_, но позже они отступили и теперь не дают рекомендаций по частным полям. В справочном источнике .NET часто используются m_ префиксы; FWIW в C # я использую как this, так и префикс для полей. Одного IMO this недостаточно, потому что компилятор не выдаст ошибку, если вы его забудете, и вы можете очень легко вызвать StackOverflowException, если попытаетесь установить поле поддержки в установщике свойства. - person jrh; 26.04.2019

Многие люди используют это, потому что в их среде IDE будет появляться список идентификаторов текущего класса.

Я знаю, что участвую в BCB.

Я думаю, что приведенный вами пример конфликта имен является исключением. Однако в Delphi рекомендации по стилю используют префикс (обычно «a») для параметров, чтобы избежать именно этого.

person Marco van de Voort    schedule 29.06.2009
comment
Обычно быстрее набирать Ctrl + Пробел или любой другой ярлык автозаполнения для вашей IDE; Visual Studio обычно этого не требует - ›чтобы увидеть методы и переменные в области видимости. - person Pete Kirkham; 29.06.2009
comment
+1 по наиболее вероятной причине, почему я так часто вижу это в кодовой базе. Интересно, что IDE побуждает людей писать код иначе, чем в более простом редакторе. - person SmacL; 29.06.2009
comment
@ Пит, согласен, но я думаю, Марко, вероятно, объяснил причину, по которой это происходит так часто. - person SmacL; 29.06.2009
comment
Пит: BCB / Delphi тоже, но это дает только идентификаторы в классе, а не все в области видимости. - person Marco van de Voort; 29.06.2009

Лично я считаю, что бороться с существующим соглашением о кодировании - это то, чего вам не следует делать. Как пишет Саттер / Александреску в своей книге «Соглашения о кодировании на C ++»: не переживайте по мелочам. Кто угодно может прочитать одно или другое, независимо от того, идет ли оно в начале "this->", "_" или что-то еще.

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

Если вы все-таки обнаружите, что для его изменения есть веская причина, не делайте этого вручную. В лучшем случае ваша IDE поддерживает такие «рефакторинги». В противном случае напишите сценарий его изменения. Последним вариантом должен быть поиск и замена. В любом случае у вас должна быть резервная копия (система контроля версий) и какое-то автоматизированное средство тестирования. Иначе вы не получите от этого удовольствия.

person Community    schedule 30.06.2009

Использование this таким образом - это не запах кода, а просто личное предпочтение. Поэтому это не так важно, как согласованность с остальным кодом в системе. Если этот код несовместим, вы можете изменить его, чтобы он соответствовал другому коду. Если, изменив его, вы внесете несогласованность с большей частью остального кода, это очень плохо, и я бы оставил это в покое.

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

person markh44    schedule 29.06.2009

Я всегда использую соглашение об именах m_. Хотя мне в целом не нравится «венгерская нотация», мне очень полезно очень четко видеть, работаю ли я с данными членов класса. Кроме того, я обнаружил, что использование двух одинаковых имен переменных в одной области слишком подвержено ошибкам.

person Dimitri C.    schedule 29.06.2009

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

person 1800 INFORMATION    schedule 29.06.2009

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

person Dario Solera    schedule 29.06.2009

class MyClass
{
public:
  int m_x;
  void f(int p_x);
};


void MyClass::f(int p_x)
{
  m_x = p_x;
}

... я предпочитаю использовать префиксы области видимости. m_ для члена, p_ для параметра (некоторые вместо этого используют a_ для аргумента), g_ для глобального и иногда l_ для локального, если это помогает читаемости.

Если у вас есть две переменные, которые заслуживают одного и того же имени, это может очень помочь и позволяет избежать случайного изменения ее значения, чтобы избежать переопределения. Или, что еще хуже, ужасные «x2, x3, x4 и т. Д.» ...

person Dan Brown    schedule 29.06.2009
comment
Я часто видел, что p_ используется для обозначения указателя в C ++, что может сбивать с толку. - person g .; 29.06.2009

В C ++ более нормально инициализировать элементы при построении с использованием инициализатора.

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

Так что, хотя я бы использовал Foo(int x) { this.x = x; } в Java, я бы не стал использовать C ++.

Настоящий запах может заключаться в отсутствии использования инициализаторов и методов, которые не делают ничего, кроме изменения переменных-членов, а не в использовании самого this -> x.

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

person Pete Kirkham    schedule 29.06.2009
comment
Нет, не надо: Foo :: Foo (int x): x (x) {} // работает как задумано, первый и третий x - аргумент, второй x - это- ›x - person MSalters; 29.06.2009

Сегодня большинство редакторов IDE раскрашивают ваши переменные, чтобы указать члены класса локальных переменных. Таким образом, IMO, ни префиксы, ни this-> не требуются для удобочитаемости.

person Thomas Fauskanger    schedule 29.06.2015

Мне не нравится использовать «это», потому что это атавистично. Если вы программируете на старом-добром C (помните C?) И хотите имитировать некоторые характеристики ООП, вы создаете структуру с несколькими членами (они аналогичны свойствам вашего объекта) и создаете набор функций, которые все принимают указатель на эту структуру в качестве своего первого аргумента (они аналогичны методам этого объекта).

(Я думаю, что этот синтаксис typedef правильный, но это было давно ...)

typedef struct _myclass
{
   int _x;
} MyClass;

void f(MyClass this, int x)
{
   this->_x = x;
}

На самом деле я считаю, что старые компиляторы C ++ действительно скомпилируют ваш код в приведенную выше форму, а затем просто передадут его компилятору C. Другими словами - C ++ в какой-то степени был просто синтаксическим сахаром. Поэтому я не уверен, зачем кому-то программировать на C ++ и возвращаться к явному использованию «this» в коде - возможно, это «синтаксический Nutrisweet».

person eeeeaaii    schedule 29.06.2009

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

class tea
{
public:
    int cup;
    int spoon;
    tea(int cups, int spoons);
};

or

class tea
{
public:
    int cup;
    int spoon;
    tea(int drink, int sugar);
};

Думаю, вы уловили идею. Это в основном именование переменных разными, но «одинаковыми» в логическом смысле. Я надеюсь, что это помогает.

person Secko    schedule 29.06.2009
comment
Я принял вопрос за что-то другое. Теперь я вижу, что вы действительно хотели узнать другой подход к обозначениям. - person Secko; 29.06.2009

person    schedule
comment
Он не назвал параметр функции таким же, как переменная-член, поэтому не перегрузил имя и не заставил это делать- › - person Pod; 29.06.2009
comment
Не думаю, что это приносит какие-то полезные аргументы в обсуждение - person Newtopian; 15.07.2009