Навіщо використовувати префікси змінних членів у класах C ++


150

Багато коду C ++ використовують синтаксичні умови для позначення змінних членів. Загальні приклади включають

  • m_ memberName для публічних членів (де публічні члени взагалі використовуються)
  • _ memberName для приватних членів або всіх членів

Інші намагаються примусити використовувати цей-> член кожного разу, коли використовується змінна.

На мій досвід, більшість базових кодів не вдається послідовно застосовувати такі правила.

В інших мовах ці конвенції набагато менш поширені. Я бачу це лише зрідка в Java або C # коді. Я думаю, я ніколи не бачив цього в коді Ruby або Python. Таким чином, здається, що в сучасних мовах існує тенденція не використовувати спеціальну розмітку для змінних членів.

Чи корисна ця конвенція сьогодні в C ++ чи це просто анахронізм. Тим більше, що він використовується так непослідовно в бібліотеках. Хіба не показано, що інші мови можуть обійтися без префіксів члена?


15
Я віддаю перевагу; У складних кодових базах може бути важливо знати, які варіанти локальні, а які - ні. Я, як правило, використовую префікс for pristing this-> що, як мені здається, багато зайвого введення та необов’язкового (тоді як іменування змусить вас це зробити)
Joe

5
Ви ніколи не бачили його в Ruby через атрибут @ for і ідіому створення генераторів, що надають перевагу використанню атрибутів безпосередньо.
Стів Джессоп

6
У відповідності з PEP 8 змінних непублічних членів повинні мати префікс підкреслення в Python (приклад: self._something = 1).
Натан Осман

2
Чи не слід виділяти синтаксис редактора для їх ідентифікації?
теж

2
Ви дійсно бачили еквівалент this->memberу коді Python. У Python зазвичай це буде, self.memberі це не лише умова, це вимагається мовою.
matec

Відповіді:


48

Ви повинні бути обережними з використанням провідного підкреслення. Перша підкреслення перед великою літерою в слові зарезервована. Наприклад:

_Foo

_L

усі зарезервовані слова поки

_фу

_l

не. Існують й інші ситуації, коли провідні підкреслення перед малими літерами заборонені. У моєму конкретному випадку я виявив, що _L було зарезервовано Visual C ++ 2005, і зіткнення створило несподівані результати.

Я на паркані про те, наскільки корисно маркувати локальні змінні.

Ось посилання про те, які ідентифікатори зарезервовані: Які правила використання підкреслення в ідентифікаторі C ++?


4
Власне, і _foo, і _l зарезервовані в області простору імен.

13
Але вони нормальні як імена змінних членів. Я не наголошую на підкресленнях, тому що правила занадто заплутані, і я раніше був спалений.
Хуан

11
Це не застережені слова. Вони є зарезервованими іменами. Якби вони були зарезервованими словами, ви взагалі не могли їх використовувати. Оскільки вони є зарезервованими іменами, ви можете використовувати їх, але на свій страх і ризик.
TonyK

235

Я всім прихильниками префіксів, зроблених добре .

Я думаю, що (Система) угорська нотація відповідає за більшість «поганих репів», які отримують префікси.

Це позначення є значною мірою безглуздим у сильно набраних мовах, наприклад, на C ++ "lpsz", щоб сказати вам, що ваш рядок є довгим покажчиком на завершений нулем рядок, коли: сегментована архітектура є давньою історією, рядки C ++ є загальними указівками конвенції на нульове завершення масиви char, і насправді не все так складно знати, що "customerName" - це рядок!

Однак я використовую префікси, щоб вказати на використання змінної (по суті, "Угорська програми", хоча я вважаю за краще уникати терміна "Угорська" через те, що вона має погану та несправедливу зв'язок із системою Угорська), і це дуже зручно для економії часу та підхід до зменшення помилок

Я використовую:

  • м для членів
  • c для констант / readonlys
  • p для вказівника (і pp для вказівника на вказівник)
  • v для мінливих
  • s для статики
  • i для індексів та ітераторів
  • е для подій

У тих випадках, коли я бажаю зробити тип зрозумілим, я використовую стандартні суфікси (наприклад, Список, ComboBox тощо).

Це дає зрозуміти програмісту про використання змінної, коли вони бачать / використовують її. Напевно, найважливіший випадок - "p" для вказівника (оскільки використання змінюється від var. На var->, і вам потрібно бути набагато обережнішими вказівниками - NULL, арифметикою вказівника тощо), але всі інші дуже зручні.

Наприклад, ви можете використовувати одне і те ж ім’я змінної декількома способами в одній функції: (тут приклад C ++, але він застосовується однаково до багатьох мов)

MyClass::MyClass(int numItems)
{
    mNumItems = numItems;
    for (int iItem = 0; iItem < mNumItems; iItem++)
    {
        Item *pItem = new Item();
        itemList[iItem] = pItem;
    }
}

Ви можете побачити тут:

  • Немає плутанини між членом і параметром
  • Немає плутанини між індексом / ітератором та елементами
  • Використання набору чітко пов'язаних змінних (список елементів, вказівник та індекс), які уникають безлічі підводних каменів загальних (розпливчастих) назв, таких як "count", "index".
  • Префікси зменшують введення тексту (коротше та краще працювати з автоматичним завершенням), ніж альтернативи, такі як "itemIndex" та "itemPtr"

Ще одним чудовим моментом ітераторів "iName" є те, що я ніколи не індексую масив з неправильним індексом, і якщо я копіюю цикл всередині іншого циклу, мені не доведеться переробляти одну зі змінних індексу циклу.

Порівняйте цей нереально простий приклад:

for (int i = 0; i < 100; i++)
    for (int j = 0; j < 5; j++)
        list[i].score += other[j].score;

(що важко читати і часто призводить до використання "i" там, де призначено "j")

з:

for (int iCompany = 0; iCompany < numCompanies; iCompany++)
    for (int iUser = 0; iUser < numUsers; iUser++)
       companyList[iCompany].score += userList[iUser].score;

(що набагато читає і знімає всі плутанини щодо індексації. Завдяки автоматичному заповненню в сучасних ІДЕ це також швидко та легко набрати)

Наступна перевага полягає в тому, що фрагменти коду не потребують розуміння будь-якого контексту . Я можу скопіювати два рядки коду в електронну пошту чи документ, і кожен, хто читає цей фрагмент, може визначити різницю між усіма членами, константами, покажчиками, індексами тощо. Мені не потрібно додавати "о, і будьте обережні, оскільки 'data' - вказівник на покажчик ", тому що він називається 'ppData'.

І з тієї ж причини мені не потрібно виводити очі з рядка коду, щоб зрозуміти це. Мені не потрібно шукати код, щоб виявити, чи є "дані" локальними, параметрами, членами або константами. Мені не потрібно рухати рукою миші, щоб я міг навести вказівник на "дані", а потім чекати появи спливної підказки (яка іноді ніколи не з'являється). Тож програмісти можуть читати та розуміти код значно швидше, оскільки вони не витрачають час на пошук вгору та вниз чи на очікування.

(Якщо ви не думаєте, що ви витрачаєте час на пошук вгору і вниз, щоб розібратися, знайдіть код, який ви написали рік тому, і з того часу не переглядали. Відкрийте файл і стрибайте приблизно на півдорозі, не читаючи його. Дивіться, як далеко ви можете прочитати з цього моменту, перш ніж ви не знаєте, чи є щось членом, параметром чи локальним. Тепер перейдіть до іншого випадкового місця ... Це те, що ми всі робимо цілий день, коли ми один раз перебираємо чийсь код або намагаються зрозуміти, як викликати їх функцію)

Префікс 'm' також дозволяє уникнути негарних та багатослівних позначень "this->" (IMHO) та невідповідності, яку він гарантує (навіть якщо ви обережні, зазвичай ви стикаєтесь із сумішшю "this-> data" та 'data' в одному класі, тому що ніщо не примушує послідовного написання імені).

Позначення "це" покликане вирішити двозначність - але чому б хтось свідомо писав код, який може бути неоднозначним? Неоднозначність буде приводити до помилки , рано чи пізно. І в деяких мовах "це" не можна використовувати для статичних членів, тому вам доведеться ввести "особливі випадки" у вашому стилі кодування. Я вважаю за краще єдине просте правило кодування, яке застосовується скрізь - явне, однозначне та послідовне.

Остання основна вигода - це Intellisense та автоматичне завершення. Спробуйте використовувати Intellisense у формі Windows для пошуку події - вам потрібно прокрутити сотні таємничих методів базового класу, які вам ніколи не потрібно буде викликати, щоб знайти події. Але якби кожна подія мала префікс "е", вони автоматично перераховувалися б у групу під "е". Таким чином, префіксація працює на групування членів, конкурсів, подій тощо у списку інтелігенції, що робить набагато швидшим та простішим пошук потрібних імен. (Зазвичай метод може мати близько 20-50 значень (локальні, параметри, члени, consts, події), які є доступними за своїм обсягом. Але після введення префікса (я хочу зараз використовувати індекс, тому я набираю 'i. .. '), мені представлено лише 2-5 варіантів автоматичного завершення.

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


Аргументи проти

Отже, які мінуси? Типовими аргументами проти префіксів є:

  • "Префіксні схеми - погані / злі" . Я погоджуюсь, що "m_lpsz" та його лайка погано продумані та абсолютно марні. Ось чому я б радив використовувати чітко розроблені позначення, розроблені для підтримки ваших вимог, а не копіювати щось, що не відповідає вашому контексту. (Використовуйте правильний інструмент для роботи).

  • "Якщо я зміню використання чогось, мені доведеться перейменувати його" . Так, звичайно, ви це робите, саме про це рефакторинг, і чому у IDE є інструменти рефакторингу, щоб зробити цю роботу швидко і безболісно. Навіть без префіксів зміна використання змінної майже напевно означає, що її назву слід змінити.

  • "Префікси просто бентежать мене" . Як і кожен інструмент, поки ви не навчитесь ним користуватися. Після того, як ваш мозок звик до моделей імен, він автоматично відфільтрує інформацію, і ви не подумаєте, що префікси вже є. Але вам доведеться твердо використовувати таку схему протягом тижня-двох, перш ніж ви дійсно станете "вільними". І ось тоді багато людей дивляться на старий код і починають замислюватися, як їм колись вдавалося без гарної префіксальної схеми.

  • "Я можу просто подивитися на код, щоб опрацювати цей матеріал" . Так, але вам не потрібно витрачати час на пошуки в іншому місці коду чи запам’ятовування його дрібниць, коли відповідь є правильним на місці, на яке вже зосереджено ваше око.

  • (Деякі з) цю інформацію можна знайти, просто чекаючи, коли підсказка з’явиться на моїй змінній . Так. Якщо це підтримується, для деяких типів префікса, коли ваш код складено чисто, після чекання ви можете прочитати опис та знайти інформацію, яку префікс передав би миттєво. Я відчуваю, що префікс - це простіший, надійніший та ефективніший підхід.

  • "Це більше вводити текст" . Дійсно? На один цілий персонаж більше? Або це так - за допомогою інструментів автоматичного завершення IDE часто зменшується введення тексту, оскільки кожен символ префіксу значно звужує простір пошуку. Натисніть "e", і три події у вашому класі спливуть у взаємозвученні. Натисніть "c", і п'ять констант у списку.

  • "Я можу використовувати this->замість m" . Ну так, можна. Але це просто набагато потворніший і більш багатослівний префікс! Тільки це несе набагато більший ризик (особливо в командах), оскільки для компілятора це необов'язково , а тому його використання часто непослідовно. mз іншого боку, це коротка, чітка, явна та необов'язкова, тому набагато важче помилитися, використовуючи її.


6
Я маю на увазі, що я читав, що проблема з нотарією угорців якраз була наслідком того, що Сімоні був неправильно зрозумілий. Він написав префікс, який слід використовувати для вказівки типу змінної, де він мав на увазі "тип", як у "вид речі", а не буквальний тип даних. Пізніше хлопці з платформи в Microsoft підхопили його і придумали lpsz ... а решта - історія ...
VoidPointer

19
"s - це для статичних", схоже, схоже на "погану" форму угорської для мене.
jalf

6
@Mehrdad: Я не думаю z, що дуже часто корисний у такій мові, як C ++, де така деталізація щодо низького рівня повинна бути інкапсульована у класі, але в C (де нульове закінчення є важливою відмінністю) я згоден з вами. ІМО будь-яку схему, яку ми використовуємо, слід адаптувати так, як це потрібно, щоб найкращим чином відповідати нашим власним потребам. Отже, якщо нульове припинення впливає на ваше використання змінної, немає нічого поганого в оголошенні "z" корисним префіксом.
Джейсон Вільямс

14
The most important case is "p" for pointer (because the usage changes from var. to var-> and you have to be much more careful with pointers.Я цілком від душі не згоден. Якщо я неправильно використовую вказівник, він просто не компілюється ( void*може бути винятком і для подвійних покажчиків). І все ->більше .досить , щоб сказати мені це покажчик. Крім того, якщо ви використовуєте автозаповнення, ваш редактор, ймовірно, має підказки щодо декларування, що виключає необхідність префіксації змінної інформації. Незалежно, хороша відповідь.
Томас Едінг

5
Прихильний до чітких, вичерпних та цікавих пояснень, однак тут мало що говорить про те, як це економить час на C ++ YET, залишається в основному невикористаним у багатьох інших мовах.

115

Я зазвичай не використовую префікс для змінних членів.

Я мав звичай використовувати mпрефікс, поки хто - то вказав, що «C ++ вже має стандартний префікс для доступу до члену: this->.

Тож саме цим я зараз користуюся. Тобто, коли є двозначність , я додаю this->префікс, але зазвичай, двозначності не існує, і я можу просто посилатися безпосередньо на ім’я змінної.

Для мене це найкраще з обох світів. У мене є префікс, який я можу використовувати, коли мені це потрібно, і я вільний залишати його, коли це можливо.

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

На що я кажу "так що? Якщо вам потрібно це знати, у вашого класу, ймовірно, є занадто великий стан. Або функція занадто велика і складна".

На практиці я виявив, що це працює надзвичайно добре. Як додатковий бонус, це дозволяє мені легко просувати локальну змінну до члена класу (або навпаки) без необхідності перейменовувати її.

А найкраще - це послідовно! Мені не потрібно робити нічого особливого і не пам'ятати будь-яких умов, щоб зберегти послідовність.


До речі, не слід використовувати провідні підкреслення для членів класу. Ви отримуєте незручно близькі до імен, які зарезервовані реалізацією.

Стандарт резервує всі імена, починаючи з подвійного підкреслення або підкреслення, а потім великої літери. Він також зберігає всі імена, починаючи з одного підкреслення в глобальному просторі імен .

Таким чином, учасник класу з основним підкресленням, за яким йде маленька літера, є законним, але рано чи пізно ви збираєтесь зробити те ж саме з ідентифікатором, починаючи з верхнього регістру, або іншим чином порушите одне з вищезазначених правил.

Так що простіше просто уникати провідних підкреслень. Використовуйте підкреслення постфікса m_або mпрефікс або просто, якщо ви хочете кодувати область в імені змінної.


"Таким чином, учасник класу з основним підкресленням, за яким йде маленька літера, є законним, але рано чи пізно ви збираєтесь зробити те ж саме з ідентифікатором, починаючи з верхнього регістру, або іншим чином порушите одне з вищезазначених правил." - змінних членів класу немає у глобальному просторі імен, тому провідне підкреслення є безпечним, незалежно від того, чи йде за ним нижня або велика літера.
mbarnett

3
@mbarnett: Ні, підкреслення, за яким йде верхній регістр, зарезервовано загалом , а не лише у глобальному просторі імен.
джельф

9
здивовано, що голос цієї відповіді менший за приставку.
Марсон Мао

Я погоджуюся з цією відповіддю, просто використовуйте, this->якщо вам потрібно вказати, що її змінну члена, чи ні, це теж добре.
Девід Мортон

Крім того, вам не потрібно документувати свою умову, щоб надавати свій код іншим людям. Усі розуміють, що this->означає.
Кадушон

34

Я віддаю перевагу підкресленням постфікса, як-от такі:

class Foo
{
   private:
      int bar_;

   public:
      int bar() { return bar_; }
};

Я також. Я також даю однойменні аксесуари / мутатори.
Роб

4
Цікаво. Спочатку виглядає трохи некрасиво, але я бачу, як це може бути корисно.
ya23

6
Я б сказав, що це набагато менш потворно, ніж: "mBar" або "m_bar".
січня

6
але тоді у вас є vector<int> v_;і написання v_.push_back(5)дуже потворно
авим

4
Це стиль Google C ++.
Justme0

20

Останнім часом я прагну віддавати перевагу префіксу m_ замість того, щоб не мати префікса взагалі, причини не стільки в тому, що його важливо позначити змінні учасника, але в тому, що він уникає неоднозначності, скажімо, у вас є код типу:

void set_foo(int foo) { foo = foo; }

Причина не працює, fooдозволена лише одна . Отже, ваші варіанти:

  • this->foo = foo;

    Мені це не подобається, оскільки це спричиняє затінення параметрів, ви більше не можете використовувати g++ -Wshadowпопередження, його також довше вводити m_. Ви також все ще стикаєтеся з іменними конфліктами між змінними та функціями, коли у вас є a int foo;і a int foo();.

  • foo = foo_; або foo = arg_foo;

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

  • m_foo = foo;

    Документація API залишається чистою, у вас не виникає неоднозначності між функціями-членами та змінними та її коротше набирати потім this-> . Єдиним недоліком є ​​те, що він робить структури POD некрасивими, але оскільки структури POD в першу чергу не страждають від неоднозначності імен, не потрібно використовувати їх разом з ними. Наявність унікального префіксу також спрощує кілька операцій пошуку та заміни.

  • foo_ = foo;

    Більшість переваг m_застосовано, але я відкидаю це з естетичних міркувань, а останній підкреслення лише робить змінну неповною і незбалансованою. m_просто виглядає краще. Використання m_також є більш розширеним, як ви можете використовувати g_для глобальних таs_ для статики.

PS: Причина, по якій ви не бачите m_в Python або Ruby, полягає в тому, що обидві мови застосовують свій власний префікс, Ruby використовує @для змінних членів і Python вимагає self..


1
щоб бути чесним, ви пропустили щонайменше 2 інші варіанти, наприклад (a) використовуйте повні імена, наприклад fooлише для членів, і замість цього використовуєте однобуквені або короткі імена для параметрів або інших місцевих жителів / викидів, наприклад int f; або (b) приєднати параметри чи інші локальні адреси з чимось. Хороша крапка m_і стручки, хоча; Я самостійно дійшов переваги дотримуватися обох цих рекомендацій, здебільшого.
підкреслити_28

12

Під час читання через функцію члена знання того, хто "володіє" кожною змінною, абсолютно важливо для розуміння значення змінної. У такій функції:

void Foo::bar( int apples )
{
    int bananas = apples + grapes;
    melons = grapes * bananas;
    spuds += melons;
}

... досить просто побачити, звідки беруться яблука та банани, а як бути з виноградом, динями та шпорами? Чи слід шукати у глобальному просторі імен? У класі декларації? Чи є змінна членом цього об'єкта чи членом класу цього об’єкта? Не знаючи відповіді на ці запитання, ви не можете зрозуміти код. А при більш тривалому функціонуванні навіть декларації локальних змінних, таких як яблука та банани, можуть загубитися в перемішці.

Попередження послідовної мітки для глобальних, змінних членів та статичних змінних членів (можливо, g_, m_ і s_ відповідно) миттєво прояснює ситуацію.

void Foo::bar( int apples )
{
    int bananas = apples + g_grapes;
    m_melons = g_grapes * bananas;
    s_spuds += m_melons;
}

Вони можуть спочатку звикнути, але потім, чого програмування не робить? Був день, коли навіть {і} дивився на тебе дивно. І як тільки ви звикнете до них, вони допоможуть вам зрозуміти код набагато швидше.

(Використовувати "this->" замість m_ має сенс, але він є ще більш затятим і візуально руйнівним. Я не вважаю це гарною альтернативою для маркування всіх видів використання змінних членів.)

Можливим запереченням проти вищезгаданого аргументу було б поширення аргументу на типи. Також може бути правдою, що знання типу змінної "абсолютно важливо для розуміння значення змінної". Якщо це так, то чому б не додати префікс до кожного імені змінної, що визначає його тип? З цією логікою ви закінчуєте угорське позначення. Але багато людей вважають, що угорська нотація є трудомісткою, потворною та недоброзичливою.

void Foo::bar( int iApples )
{
    int iBananas = iApples + g_fGrapes;
    m_fMelons = g_fGrapes * iBananas;
    s_dSpuds += m_fMelons;
}

угорський робитьскажи нам щось нове про код. Тепер ми розуміємо, що у функції Foo :: bar () є кілька неявних кастів. Проблема з кодом зараз полягає в тому, що значення інформації, доданої угорськими префіксами, є невеликою щодо візуальної вартості. Система типу C ++ включає безліч функцій, які допомагають типам або працювати добре разом, або викликати попередження або помилку компілятора. Компілятор допомагає нам мати справу з типами - для цього нам не потрібні позначення. Ми можемо досить легко зробити висновок, що змінні у Foo :: bar (), ймовірно, є числовими, і якщо це все, що ми знаємо, це досить добре для отримання загального розуміння функції. Тому значення знання точного типу кожної змінної є відносно низьким. І все ж потворність змінної типу "s_dSpuds" (або навіть просто "dSpuds") велика. Так,


Дякую за ідею s_. Здається, дуже корисно, і мені якось ніколи не приходило в голову.
Кріс Олсен

10

Я не можу сказати, наскільки це широкоспроможне, але кажучи особисто, я завжди (і завжди) робив префікси своїх змінних членів символом "m". Наприклад:

class Person {
   .... 
   private:
       std::string mName;
};

Це єдина форма префіксів, яку я використовую (я дуже проти угорської нотації), але вона добре витримувала мене протягом багатьох років. Як осторонь, я, як правило, заперечую використання підкреслення в іменах (або деінде для цього), але роблю виняток для макроімен препроцесорів, оскільки вони, як правило, є великими літерами.


5
Проблема з використанням m, а не m_ (або _) полягає в поточній моді на випадок верблюдів, що ускладнює читання деяких імен змінних.
Мартін Бекетт

1
@Neil Я з тобою. @mgb: Я ненавиджу імена, починаючи з "_" Це лише запрошення, щоб справи пішли не так у майбутньому.
Мартін Йорк

1
@Neil: Яку умову ви використовуєте тоді, якщо ви не використовуєте підкреслення і не використовуєте верблюд?
jalf

2
Моє розуміння полягало в тому, що саме camelCase змушує використовувати просто m для змінних, таких як "apData", що плутають - це стає "mapData", а не "m_apData". Я використовую _camelCase для захищених / приватних змінних членів, оскільки він виділяється
Martin Beckett

10
@MartinBeckett: У aцьому сценарії слід скористатися великим рахунком - ви не робите це правильно інакше. mApData( mпрефікс, тоді ім'я змінної є apData).
Platinum Azure

8

Основна причина префікса члена - це розрізняти локальну функцію члена та змінну члена з тим самим іменем. Це корисно, якщо ви користуєтеся геттерами з назвою речі.

Поміркуйте:

class person
{
public:
    person(const std::string& full_name)
        : full_name_(full_name)
    {}

    const std::string& full_name() const { return full_name_; }
private:
    std::string full_name_;
};

У цьому випадку змінну члена не можна було назвати full_name. Вам потрібно перейменувати функцію члена, щоб отримати get_full_name () або якось прикрасити змінну члена.


1
Це є причиною я префікса. Я думаю, що foo.name()це набагато читабельніше, ніж foo.get_name()на мою думку.
Терабіти

6

Я не думаю, що один синтаксис має реальне значення над іншим. Все це зводиться, як ви вже згадували, до рівномірності в усіх вихідних файлах.

Єдиний момент, коли мені такі правила цікаві, це коли мені потрібні 2 речі, названі ідентичні, наприклад:

void myFunc(int index){
  this->index = index;
}

void myFunc(int index){
  m_index = index;
}

Я використовую це для розмежування двох. Крім того, коли я завершую виклики, як, наприклад, з Windows Dll, RecvPacket (...) з Dll може бути загорнутий у RecvPacket (...) у моєму коді. У цих конкретних випадках використання префікса типу "_" може зробити так, щоб вони виглядали однаково, легко визначити, що є, але відрізняється для компілятора


6

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

Я знав програмістів, яким незручно використовувати локальні декларації; вони вважають за краще розміщувати всі декларації у верхній частині блоку (як у С), тому знають, де їх знайти. Я виявив, що там, де дозволяє це масштабування, оголошення змінних там, де вони вперше використовуються, зменшує час, який я витрачаю на огляд назад, щоб знайти декларації. (Це справедливо для мене навіть для невеликих функцій.) Це полегшує мені розуміння коду, який я шукаю.

Я сподіваюся, що досить зрозуміло, як це стосується конвенцій про іменування членів: Коли члени мають однакову префіксну форму, я ніколи не повинен оглядатися взагалі; Я знаю, що декларація навіть не знайдеться у вихідному файлі.

Я впевнений, що я не почав віддавати перевагу цим стилям. Але з часом, працюючи в середовищах, де їх застосовували послідовно, я оптимізував своє мислення, щоб ними скористатися. Я думаю, що можливо, багато людей, які наразі почувають себе некомфортно, також приходять до вподобань, якщо їх використовувати.


5

Ці конвенції є саме цим. Більшість магазинів використовують правила коду для полегшення читабельності коду, тому кожен може легко переглядати фрагмент коду та швидко розшифровувати речі, такі як державні та приватні члени.


"між речами, такими як державні та приватні члени" - наскільки насправді це спільне? я не пригадую, щоб його бачив, але потім знову не переглядаю кодові бази чи що-небудь інше.
підкреслюйте_28

Я не роблю це за власним кодуванням, але я працював у місцях, де нам довелося це робити, виходячи з їхніх посібників з конвенції про код. Я вважаю за краще не робити цього, оскільки майже всі IDE будуть показувати приватним змінним іншого кольору.
Містер Вілл

Хм, я думаю, це відбувається лише в інших ситуаціях, ніж у мене. Зазвичай я використовую або classes, усі члени яких є private/ protected, або POD struct, всі змінні яких є public(а часто також є const). Отже, мені ніколи не потрібно замислюватися про рівень доступу будь-якого учасника.
підкреслюй_d

5

Інші намагаються примусити використовувати цей-> член кожного разу, коли використовується змінна

Це зазвичай тому, що префікса немає . Компілятору потрібна достатня кількість інформації для вирішення відповідної змінної, будь то унікальне ім'я через префікса або за допомогою thisключового слова.

Отже, так, я думаю, префікси все ще корисні. Я, наприклад, вважаю за краще ввести "_" для доступу до члена, а не "this->".


3
компілятор може вирішити це будь-коли ... локальні змінні приховуватимуть їх у більшому обсязі у більшості мов. Це для (сумнівної) користі для людей, які читають код. Будь-який гідний ІДЕ по-різному висвітлить місцевих жителів / членів / глобалістів, тому немає необхідності в подібних матеріалах
rmeador

1
Саме так. Місцеві жителі приховуватимуть членів класу. Розглянемо конструктор, який встановлює ці члени. Зазвичай має сенс називати параметри такими ж, як і члени.
Кент Бугаарт

6
Чому це кодовий запах? Я б сказав, що це абсолютно звичайно і розумно, особливо якщо мова йде про конструктори.
Кент Бугаарт

3
Конструктор повинен (як правило) встановлювати локальні дані у своєму списку ініціалізації. І там параметри не затінюють імена полів, але обидва є доступними - так що ви можете писатиstruct Foo { int x; Foo(int x) : x(x) { ... } };
Павло Мінаєв

2
Я припускаю, що проблема з цим виникає, коли ви робите, Foo(int x, bool blee) : x(x) { if (blee) x += bleecount; } // oops, forgot this->я вважаю за краще називати змінні свого члена чимось корисним, а потім давати конструкторські параметри, які відповідають їм скороченим іменам:Foo(int f) : foo(f) {...}
Стів Джессоп,

4

Інші мови використовуватимуть умови кодування, вони просто відрізняються. Наприклад, у C # є, мабуть, два різних стилі, якими користуються люди, або один із методів C ++ (_ змінний, mVariable або інший префікс, наприклад, угорська нотація), або те, що я називаю методом StyleCop.

private int privateMember;
public int PublicMember;

public int Function(int parameter)
{
  // StyleCop enforces using this. for class members.
  this.privateMember = parameter;
}

Зрештою, це стає тим, що люди знають, і що виглядає найкраще. Я особисто думаю, що код є більш зрозумілим без угорських позначень, але можна знайти простіше знайти змінну з intellisense, наприклад, якщо додається угорська нотація.

У моєму прикладі вище, вам не потрібен префікс m для змінних членів, оскільки префіксування вашого використання цим. вказує те саме, що застосовується компілятором.

Це не обов'язково означає, що інші методи погані, люди дотримуються того, що працює.


3

Коли у вас є великий метод або блоки коду, зручно негайно знати, чи використовується локальна змінна чи член. це уникати помилок і для кращої чіткості!


3
Якщо у вас є великий метод, для кращої чіткості розбийте його.
sbi

4
Є багато причин, щоб не зламати деякі великі методи. Наприклад, якщо вашому методу потрібно зберігати багато локального стану, вам або доведеться передати безліч параметрів у підпорядковані методи, створити нові класи, які існують виключно для передачі даних між цими методами або оголошення державних даних як дані членів батьківського класу. Усі вони мають проблеми, які впливатимуть на чіткість або ремонтопридатність методу, порівняно з одним методом, який давно віддалений (особливо тим, чия логіка є прямолінійною).
Стів Броберг

3
@sbi: Правила - це саме те; вказівки, а не правила. Іноді потрібні великі методи, які логічно не піддаються розбиттю, а іноді імена параметрів стикаються з членами.
Ед С.

Будь ласка, не публікуйте змінні своїх членів. Просто використовуйте аксесуари. Дужки повинні сказати читачеві, що це змінна член.
jkeys

Зауважте, що в gcc (> = 4.6) є попередження про виявлення зіткнення імен:-Wshadow
Caduchon

3

ІМО, це особисте. Я взагалі не ставлю префіксів. У будь-якому випадку, якщо код погрожує публічним, я вважаю, що він повинен мати деякі префікси, щоб він міг бути більш читабельним.

Часто великі компанії використовують власні так звані «правила розробника».
Btw, найсмішніший, але найрозумніший, що я бачив, був DRY KISS (Dont Repeat Yourself. Keep It Simple, Stupid). :-)


3

Як уже говорили інші, важливо бути розмовними (адаптувати стилі імена імен іменувань до кодової бази, в якій ви пишете) та бути послідовною.

Протягом багатьох років я працював над великою базою коду, яка використовує як конвенцію "це->", так і використовуючи позначення підкреслення постфікса для змінних членів. Протягом багатьох років я також працював над більш дрібними проектами, в деяких з яких не було ніякої конвенції щодо іменування змінних членів, а в інших, які мали різні умовні позначення змінних членів. З цих менших проектів я постійно вважаю, що найскладніше швидко і зрозуміти такі проекти, яким не вистачало будь-якої конвенції.

Я дуже анально затримуюсь щодо називання. Я буду агонізувати над тим, щоб ім’я було віднесено до класу чи змінної до того, що, якщо я не можу придумати щось, що я вважаю «хорошим», я вирішу назвати це чимось безглуздим та надам коментар із описом того, що це насправді є. Таким чином, принаймні назва означає саме те, що я маю на увазі - нічого більше і нічого менше. І часто, користуючись нею деякий час, я виявляю, що насправді має бути назва бути і можу повернутися назад, змінити або змінити рефактор належним чином.

Останнє питання на тему того, як ІДЕ виконує роботу - це все добре і добре, але ІДЕ часто недоступні в середовищах, де я виконую найневідкладнішу роботу. Іноді єдине, що доступно в цей момент, - це копія "vi". Також я бачив багато випадків, коли завершення коду IDE поширювало дурість, таку як неправильне написання імен. Таким чином, я вважаю за краще не покладатися на милицю IDE.


3

Початкова ідея префіксів для змінних членів C ++ полягала в тому, щоб зберігати додаткову інформацію про тип, про яку компілятор не знав. Так, наприклад, у вас може бути рядок, яка має фіксовану довжину символів, і інша, яка є змінною і закінчується символом '\ 0'. У компілятора вони обоє char *, але якщо ви спробуєте скопіювати з одного в інший, у вас виникнуть великі проблеми. Отже, з моєї голови,

char *aszFred = "Hi I'm a null-terminated string";
char *arrWilma = {'O', 'o', 'p', 's'};

де "asz" означає, що ця змінна - "рядок ascii (з нульовим завершенням)", а "arr" означає, що ця змінна є масивом символів.

Тоді трапляється магія. Компілятор буде цілком задоволений цим твердженням:

strcpy(arrWilma, aszFred);

Але ви, як людина, можете подивитися на це і сказати: «Ей, ці змінні насправді не є одним типом, я не можу цього зробити».

На жаль, у багатьох місцях використовуються стандарти, такі як "m_" для змінних членів, "i" для цілих чисел, незалежно від того, як вони використовуються, "cp" для покажчиків char. Іншими словами, вони дублюють те, що знає компілятор, і роблять код важким для читання одночасно. Я вважаю, що ця згубна практика повинна бути заборонена законом та застосовувати суворі покарання.

Нарешті, слід зазначити два моменти:

  • Розумне використання функцій C ++ дозволяє компілятору знати інформацію, яку вам довелося кодувати в сирих змінних стилю С. Ви можете робити класи, які дозволять лише дійсні операції. Це слід робити настільки ж практичним.
  • Якщо кодові блоки настільки довго , що ви забули , що тип змінної , перш ніж використовувати його, вони шлях надто довго. Не використовуйте імена, переорганізуйтесь.

Префікси, які вказують на тип чи вид змінної, - це теж щось, що варто обговорити, але я мав на увазі переважно префікси, які вказують, чи є щось (приватним) членом / полем. Зворотна угорська позначення, яку ви згадуєте, може бути дуже зручною, коли застосовується розумно (як у вашому прикладі). Мій улюблений приклад, де це має сенс - відносні та абсолютні координати. коли ви бачите absX = relX, ви можете зрозуміти, що щось може бути неправильним. Ви також можете відповідно назвати функції: absX = absFromRel (relX, offset);
VoidPointer

Примітка: ініціалізація aszFred викликає сумніви (пропонуючи доступ без доступу до прямої рядка), ініціалізація arrWilma навіть не буде компілюватися. (Ви, напевно, мали намір оголосити arrWilma як масив, а не вказівник!) Хоча немає проблем, оскільки ви писали, що це просто вгорі голови: :-)
Niels Dekker

На жаль, ви абсолютно праві. Діти, не намагайтеся цього вдома. Зробіть це: 'const char * aszFred = "Привіт, я є нульовим завершеним рядком"; char arrWilma [] = {'O', 'o', 'p', 's'}; '
А. Л. Фланаган

3

Наш проект завжди використовував "його" як префікс для даних членів, а "" - як префікс параметрів, без префікса для місцевих жителів. Це трохи люб’язно, але його було прийнято ранніми розробниками нашої системи, оскільки вони бачили, що вона використовується як конвенція деякими бібліотеками комерційних джерел, якими ми користувалися в той час (або XVT, або RogueWave - можливо, обидва). Отже, ви отримаєте щось подібне:

void
MyClass::SetName(const RWCString &theName)
{
   itsName = theName;
}

Найбільшою причиною, яку я бачу в застосуванні префіксів (і жодних інших - я ненавиджу угорську нотацію), є те, що це заважає вам потрапити в проблеми, написавши код, де ви думаєте, що ви посилаєтесь на одну змінну, але ви дійсно маєте на увазі іншу змінну з однойменною назвою, визначеною в локальному масштабі. Це також уникає проблеми створення імен змінної для представлення того самого поняття, але з різними сферами застосування, як у прикладі вище. У такому випадку вам все одно доведеться придумати якийсь префікс або іншу назву параметра "theName" - чому б не зробити послідовне правило, яке застосовується всюди.

Просто використання цього-> насправді недостатньо добре - ми не так зацікавлені у зменшенні неоднозначності, як у зменшенні помилок кодування, і маскування імен з локально визначеними ідентифікаторами може бути болем. Зрозуміло, деякі компілятори можуть мати можливість піднімати попередження для випадків, коли ви замаскували ім'я в більшій кількості, але ці попередження можуть стати неприємністю, якщо ви працюєте з великим набором сторонніх бібліотек, які, як правило, вибрали назви невикористаних змінних, які періодично стикаються з вашими власними.

Щодо самого / самого - я, чесно кажучи, мені простіше набрати, ніж підкреслення (як чіткий машиніст, я уникаю підкреслення, коли це можливо - занадто багато, що розтягується з домашніх рядків), і мені здається, це читабельніше, ніж таємничий підкреслення.


Це найінтуїтивніше рішення з найшвидшою кривою навчання, яку я коли-небудь чув. Я хотів би, щоб розмовні мови були більш гнучкими в обробці всіх тих, щоб нам не довелося думати про створення нових методів вирішення неясностей у коді.
Guney Ozsan

2

Я використовую його, тому що Intellisense VC ++ не може сказати, коли показувати приватних членів під час виходу з класу. Єдина вказівка ​​- це маленький символ "блокування" на значці поля у списку Intellisense. Це просто полегшує ідентифікацію приватних членів (полів). Також звичка від C # бути чесним.

class Person {
   std::string m_Name;
public:
   std::string Name() { return m_Name; }
   void SetName(std::string name) { m_Name = name; }
};

int main() {
  Person *p = new Person();
  p->Name(); // valid
  p->m_Name; // invalid, compiler throws error. but intellisense doesn't know this..
  return 1;
}

2

Я думаю, що якщо вам потрібні префікси для відрізнення членів класу від параметрів функції члена та локальних змінних, або функція є занадто великою, або змінні називаються погано. Якщо він не поміщається на екрані, ви можете легко побачити, що є, рефактор.

Зважаючи на те, що вони часто оголошуються далеко не там, де вони використовуються, я вважаю, що називати конвенції для глобальних констант (і глобальних змінних, хоча ІМО рідко коли-небудь виникає необхідність використовувати їх) має сенс. Але в іншому випадку я не бачу великої потреби.

Однак, я підкреслював підкреслення всіх членів приватного класу. Оскільки всі мої дані приватні, це означає, що члени мають зворотний підкреслення. Зазвичай я цього більше не роблю в нових базах коду, але оскільки ви, як програміст, ви в основному працюєте зі старим кодом, я все ще багато цього роблю. Я не впевнений, чи моя толерантність до цієї звички походить від того, що я це робив завжди і все ще роблю це регулярно, чи це справді має більше сенсу, ніж маркування змінних членів.


2
Це дуже відображає моє почуття щодо цього питання. Код повинен бути читабельним, не вдаючись до префіксів. Можливо, ми не бачимо стільки використання префіксів у більш сучасних мовах, оскільки їх спільноти користувачів сприйняли читабельність трохи більше, ніж те, що ви іноді бачите в C ++. Звичайно, C ++ можна і потрібно читати. Просто за роки написано багато нечитабельних C ++.
VoidPointer


1

За допомогою управління пам'яттю корисно розмежовувати між змінними членами та локальними змінними. Загалом кажучи, змінені членами, що виділяються купу, повинні бути знищені в деструкторі, тоді як місцеві змінні, що виділяються купу, повинні бути знищені в межах цієї межі. Застосування правила іменування до змінних членів полегшує правильне управління пам'яттю.


як так? У деструктора немає доступу до локальних змінних, оголошених в інших функціях, тому немає місця для плутанини. Крім того, місцеві змінні, виділені з купи, не повинні існувати . І змінні членів, що виділяються купу, повинні існувати лише в класах RAII, в значній мірі.
jalf

"Купі виділених локальних змінних не повинно існувати" є трохи сильним. Але якщо / коли ви їх використовуєте, це надзвичайно важливо, щоб переконатись у тому, що вони діють чітко розміщеними, тому дисциплінований конвент про іменування для члена проти локальних змінних допомагає безперечно забезпечити це.
франкстер

1

Code Complete рекомендує m_varname для змінних членів.

Хоча я ніколи не вважав, що нотація m_ корисна, я б надавав ваги думці McConnell у створенні стандарту.


2
Якщо тільки він не пояснить, чому підкреслюється. Я великий шанувальник його книги "Швидкий розвиток", яку я не раз рекомендував тут, але набагато менше "Кодексу завершено" (який, зізнаюся, я не читав з моменту його виходу).

1

Я майже ніколи не використовую префікси перед іменами змінних. Якщо ви використовуєте достатньо гідний IDE, ви повинні мати можливість переробляти та легко знаходити посилання. Я використовую дуже чіткі імена і не боюся мати довгі імена змінних. З цією філософією я ніколи не мав проблем із масштабом.

Єдиний раз, коли я використовую префікс, був би рядок підпису. Я буду префіксувати параметри методу з _, щоб я міг програмувати навколо них оборонно.


1

Такий префікс вам ніколи не потрібен. Якщо такий префікс пропонує вам будь-яку перевагу, ваш стиль кодування взагалі потребує виправлення, і це не префікс, який запобігає чистоті вашого коду. Типові погані назви змінних включають "інші" або "2". Ви не виправляєте це, вимагаючи, щоб він був іншим, ви виправляєте це, змушуючи розробника думати про те, що ця змінна робить там у контексті цієї функції. Можливо, він мав на увазі remoteSide, або newValue, або secondTestListener або щось у цьому діапазоні.

Це ефективний анахронізм, який все ще розповсюджується занадто далеко. Не припиняйте префіксувати свої змінні та дайте їм власні імена, чіткість яких відображає тривалість їх використання. До 5 рядків ви могли б називати це "я" без плутанини; за 50 рядків вам потрібно досить довге ім’я.


1

Мені подобається, що імена змінних надають лише значення значенням, які вони містять, і залишають, як вони оголошуються / реалізуються поза іменем. Я хочу знати, що означає значення, період. Можливо, я зробив більше, ніж середня кількість рефакторингу, але я вважаю, що вбудовування того, як щось реалізовано в імені, робить рефакторинг більш втомливим, ніж потрібно. Префікси, що вказують, де або як оголошуються члени об'єкта, є специфічними для реалізації.

color = Red;

Більшу частину часу мені байдуже, чи Червоний є перерахунком, структурою чи іншим, і якщо функція настільки велика, що я не можу пригадати, чи було кольором оголошено локально чи є його членом, напевно, час перерватися функція на менші логічні одиниці.

Якщо ваша цикломатична складність настільки велика, що ви не можете відслідковувати, що відбувається в коді без конкретних підказок, внесених до імен речей, швидше за все, вам потрібно буде зменшити складність вашої функції / методу.

Здебільшого я використовую лише це в конструкторах та ініціалізаторах.


0

Я використовую m_ для змінних членів лише для того, щоб скористатися Intellisense та пов'язаною з ним IDE-функціональністю. Коли я кодую реалізацію класу, я можу набрати m_ і побачити комбобокс з усіма членами m_, згрупованими разом.

Але я міг би жити без m_ без проблем, звичайно. Це просто мій стиль роботи.


Ви також можете ввестиthis->
Тост

0

Відповідно до СТАНДАРТІВ КОДУВАННЯ СПІЛЬНОГО ВІДПОВІДАЛЬНОГО ВОЗДУХНОГО ВОЗДУХА С ++ (грудень 2005 р.):

Правило АВ 67

Загальнодоступні та захищені дані слід використовувати лише у структурах, а не у класах. Обгрунтування: клас може підтримувати свій інваріант, контролюючи доступ до своїх даних. Однак клас не може контролювати доступ до своїх членів, якщо ці члени не є приватними. Отже, всі дані в класі повинні бути приватними.

Таким чином, префікс "m" стає невикористаним, оскільки всі дані повинні бути приватними.

Але корисно використовувати префікс p перед вказівником, оскільки це небезпечна змінна.


0

Багато з цих конвенцій походять із часів без складних редакторів. Я рекомендую використовувати належний IDE, що дозволяє забарвити всі види змінної. Колір набагато простіше помітити, ніж будь-який префікс.

Якщо вам потрібно отримати більш детальну інформацію про змінну, будь-який сучасний IDE повинен мати змогу показати її вам, переміщаючи каре або курсор по ній. І якщо ви використовуєте змінну неправильно (наприклад, вказівник з оператором.), Ви все одно отримаєте помилку.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.