Простір імен + функцій проти статичних методів у класі


290

Скажімо, я маю або збираюся писати набір пов'язаних функцій. Скажімо, вони пов'язані з математикою. Організаційно я повинен:

  1. Запишіть ці функції та поставте їх у мій MyMathпростір імен та посилайтесь на них черезMyMath::XYZ()
  2. Створіть клас, що називається, MyMathі зробіть ці методи статичними та зверніться до аналогічногоMyMath::XYZ()

Чому я б обрав один за іншим як засіб організації свого програмного забезпечення?


4
з одного боку, простори імен є останнім доповненням до мови, порівняно з класами та статичними методами, які були в мові з того часу, як вона називалася "C з класами". Деякі програмісти можуть бути зручнішими для старих функцій. Деякі інші програмісти можуть використовувати старі компілятори. Просто мій $ .02
Ром

21
@Rom: Ти маєш рацію щодо "старих програмістів", але помиляється щодо "старих компіляторів". Простори імен правильно складені з еонів (я працював з ними з Visual C ++ 6, починаючи з 1998 року!). Що стосується "C з класами", деякі люди на цьому форумі навіть не народилися, коли це сталося. Використання цього в якості аргументу, щоб уникнути стандартної та поширеної функції C ++, є помилкою. На закінчення, лише застарілі компілятори C ++ не підтримують простори імен. Не використовуйте цей аргумент як привід не використовувати їх.
paercebal

@paercebal: деякі стародавні компілятори досі використовуються у вбудованому світі. Непідтримка просторів імен - це, мабуть, одна з найменших незручностей, з якими потрібно стикатися під час написання коду для різних невеликих процесорів, з якими кожен взаємодіє кожен день: стереосистема, мікрохвильова піч, блок управління двигуном у вашому автомобілі, світлофор тощо. Просто для Будьте зрозумілі: я не виступаю за те, щоб не використовувати кращі, новіші компілятори скрізь. Au conrare: Я всі за новітні мовні особливості (крім RTTI;)). Я просто вказую, що така тенденція існує
Рим,

13
@Rom: У поточному випадку автор запитання має вибір, тому, мабуть, жоден із його / її компіляторів не спромігся скласти простір коду. Оскільки це питання щодо C ++, потрібно дати відповідь C ++, включаючи згадування просторів імен та рішення проблеми RTTI, якщо це необхідно. Дати відповідь С або відповідь С-з-класами для застарілих-компіляторів поза темою.
paercebal

2
"Тільки мої $ .02" - було б варто більше, якби ви надали будь-які докази наявних компіляторів C ++, які не підтримують простори імен. "деякі старовинні компілятори досі використовуються у вбудованому світі" - це нерозумно; "вбудований світ" є більш пізньою розробкою, ніж простори імен C ++. "C з класами" був введений у 1979 році, задовго до того, як хтось вбудовував код C у що-небудь. "Я просто вказую, що така тенденція існує" - навіть якщо так, це не має нічого спільного з цим питанням.
Джим Балтер

Відповіді:


243

За замовчуванням використовуйте функції простору імен.

Класи - це створення об'єктів, а не заміна просторів імен.

В об'єктно-орієнтованому коді

Скотт Майєрс написав цілий пункт для своєї ефективної книги C ++ на цю тему "Віддавайте перевагу функціям, що не належать до друзів, перед функціями членів". Я знайшов онлайн-посилання на цей принцип у статті від Herb Sutter:http://www.gotw.ca/gotw/084.htm

Важливо знати, що: У C ++ функції в тому ж просторі імен, що і клас, належать до інтерфейсу цього класу (оскільки ADL здійснюватиме пошук цих функцій при вирішенні викликів функцій).

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

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

Розширення I

Додавання коду до інтерфейсу класу.

У C # ви можете додати методи до класу, навіть якщо у вас немає доступу до нього. Але в C ++ це неможливо.

Але все ж у C ++ ви можете додати функцію простору імен, навіть до класу, який хтось написав для вас.

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

Розширення II

Побічним ефектом попереднього пункту неможливо оголосити статичні методи у кількох заголовках. Кожен метод повинен бути оголошений в одному класі.

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

Розширення III

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

#include <string>
#include <vector>

// Etc.
{
   using namespace std ;
   // Now, everything from std is accessible without qualification
   string s ; // Ok
   vector v ; // Ok
}

string ss ; // COMPILATION ERROR
vector vv ; // COMPILATION ERROR

І ви навіть можете обмежити "забруднення" одним класом:

#include <string>
#include <vector>

{
   using std::string ;
   string s ; // Ok
   vector v ; // COMPILATION ERROR
}

string ss ; // COMPILATION ERROR
vector vv ; // COMPILATION ERROR

Цей "шаблон" є обов'язковим для правильного використання майже стандартної ідіоми своп.

І це неможливо зробити зі статичними методами на заняттях.

Отже, простори імен C ++ мають власну семантику.

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

Наприклад, якщо у вас є простір імен A з функцією AAA, простір імен B з функцією BBB, ви можете оголосити простір імен C і привести AAA і BBB в цей простір імен за допомогою ключового слова.

Висновок

Простори імен призначені для просторів імен. Заняття призначені для занять.

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

Не використовуйте класи, коли вам потрібні простори імен.

І у вашому випадку вам потрібні простори імен.


Чи може ця відповідь застосовуватися і до потоків, тобто краще використовувати простори імен, а не статичні методи для потоків?
тире

3
@dashesy: простори імен порівняно зі статичними методами не мають нічого спільного з потоками, тому так, простори імен краще, оскільки простори імен майже завжди кращі, ніж статичні. Якщо одне, статичні методи мають доступ до змінних членів класу, тому вони якось мають нижче значення інкапсуляції, ніж простори імен. А ізоляція даних ще важливіша в потоковому виконанні.
paercebal

@ paercebal- дякую, я використовував методи статичного класу для функцій потоку. Тепер я розумію, що я неправильно використовував клас як простір імен, тож, на вашу думку, найкращий підхід мати кілька потоків в одному об’єкті? Я також поставив це питання на ТАК, я вдячний, якщо ви можете пролити трохи світла (тут або в самому запитанні)
dashy

1
@dashesy: ​​Ви просите проблем. Те, що ви хочете використовувати з різними потоками, - це ізолювати дані, які не повинні бути спільними, тому мати декілька потоків, які мають пільговий доступ до приватних даних класу, є поганою ідеєю. Я ховав би один потік всередині класу і обов'язково виділив дані для цього потоку від даних для основного потоку. Звичайно, дані, якими слід поділитися тоді, можуть бути членами цього класу, але вони все одно повинні бути синхронізованими (замки, атомні тощо). Я не впевнений у тому, скільки у вас є доступ, але використовувати завдання / async ще краще.
paercebal

відповідь paercebal має бути прийнятою! Просто ще одне посилання для майже стандартного обміну () через простір імен + ADL -> stackoverflow.com/questions/6380862 / ...
Gob00st

54

Є багато людей, які не погоджуються зі мною, але ось як я це бачу:

Клас - це, по суті, визначення певного виду об’єкта. Статичні методи повинні визначати операції, тісно пов'язані з цим визначенням об'єкта.

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

Наприклад, у вашому випадку запитайте себе: "Що таке MyMath?" Якщо MyMathне визначає вид об’єкта, то я б сказав: не робіть це класом.

Але, як я вже сказав, я знаю, що є багато людей, які (навіть жорстоко) не погоджуються зі мною щодо цього (зокрема, розробників Java та C #).


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

56
Ось так, адже у Jave та C # людей немає вибору.
Мартін Йорк

7
@ shog9. Ви також можете шаблонувати функції!
Мартін Йорк

6
@Dan: імовірно, той, який потребував математичних процедур і хотів підтримати "підключення" різних реалізацій.
Shog9

1
@Dan: "Я думаю, якщо хтось зацікавлений у використанні класу в якості параметра шаблону, цей клас майже напевно визначає якийсь базовий об'єкт." Ні, зовсім ні. Подумайте про риси. (Тим не менш, я повністю згоден з вашою відповіддю.)
sbi

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

В іншому випадку використовуйте функції, розділені на імена.


У відповідь на коментарі: так, статичні методи та статичні дані, як правило, надмірно використовуються. Ось чому я запропонував лише два пов’язані між собою сценарії, де я думаю, що вони можуть бути корисними. У конкретному прикладі ОП (набір математичних процедур), якщо він хотів би можливість задати параметри - скажімо, тип основних даних і точність виводу - що буде застосовано до всіх процедур, він може зробити щось на кшталт:

template<typename T, int decimalPlaces>
class MyMath
{
   // routines operate on datatype T, preserving at least decimalPlaces precision
};

// math routines for manufacturing calculations
typedef MyMath<double, 4> CAMMath;
// math routines for on-screen displays
typedef MyMath<float, 2> PreviewMath;

Якщо вам це не потрібно, то всі кошти використовувати простір імен.


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

Статичні дані не кращі, ніж глобальні простори імен.
coppro

@coppro Вони є принаймні на один крок вгору по еволюційному ланцюгу від випадкових глобальних груп, оскільки їх можна зробити приватними (але погоджуйтесь інакше).
Мартін Йорк

@Motti: OTOH, якщо ви хочете, щоб це було в заголовку (вбудовані / шаблонні функції), ви повернетеся до неприємності щодо цього.
Shog9

1
Цікавий приклад: використання класу як скорочення, щоб уникнути повторення templateаргументів!
підкреслюй_

13

Ви повинні використовувати простір імен, оскільки у простору імен є безліч переваг перед класом:

  • Не потрібно визначати все в одному заголовку
  • Вам не потрібно виставляти всю свою реалізацію в заголовку
  • Ви не usingможете користуватися класом; ти можешusing членом простору імен
  • Ви не можете using class, хочаusing namespace це не все так часто гарна ідея
  • Використання класу означає, що існує якийсь об’єкт, який потрібно створити, коли його дійсно немає

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


3
"Вам не потрібно виставляти всю свою реалізацію в заголовку", як і ви, коли ви використовуєте клас.
Вануан

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

1
@Vanuan: Ви можете розкрити реалізацію простору імен у заголовку. Просто використовуйте inlineключове слово, щоб задовольнити ODR.
Томас Едінг

@ThomasEding не потрібно! = Може
Вануан

3
@ Вануан: Під час використання компілятор гарантує лише одне inline, і це НЕ "вбудовування" тіла функції. Реальна (і гарантований стандарт) мета inlineполягає в запобіганні кількох визначень. Читайте про "Правило одного визначення" для C ++. Крім того, пов'язане запитання SO не збиралося через попередньо складені проблеми заголовка замість проблем ODR.
Томас Едінг

3

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


2
Ви можете мати приватні дані в анонімному просторі імен у файлі реалізації і з класами. Не впевнений, що я дотримуюся вашої логіки.
Патрік Джонмайєр

0

Ще одна причина використання класу - можливість використовувати специфікатори доступу. Потім, можливо, можна розбити ваш публічний статичний метод на більш дрібні приватні методи. Публічний метод може викликати кілька приватних методів.


6
Модифікатори доступу є класними, але навіть самий privateметод є більш доступним, ніж метод, прототип якого взагалі не публікується в заголовку (і, таким чином, залишається невидимим). Я навіть не згадую про кращу інкапсуляцію, запропоновану функціями анонімного простору.
paercebal

2
Приватні методи, IMO, поступаються приховуванню самої функції в реалізації (cpp-файл) і ніколи не виставляють її у файлі заголовка. Будь ласка, детальніше поясніть це у своїй відповіді та чому ви хочете використовувати приватних членів. До цього -1.
безчувальна

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

1
@ Навіть так, ви можете приховати цю інформацію всередині безіменного простору імен у .cppфайлі, який зробить її приватною для цього блоку перекладу, не надаючи зайвої інформації тому, хто читає файл заголовка. Ефективно, я намагаюся виступати за ідіому PIMPL.
безчувальна

Ви не можете помістити його у .cppфайл, якщо ви хочете використовувати шаблони.
Yay295

0

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


"[зберігання речей] у анонімному просторі імен файлу реалізації [є] більшим обсягом, ніж їх розміщення всередині класу" - ні, це не так. у випадках, коли привілейований доступ до членів не потрібен, анонімно розділені речі є більш приватними, ніж private:ті. і у багатьох випадках, коли, мабуть, потрібен пільговий доступ , це може бути усунено. найбільш "приватна" функція - це функція, яка не відображається в заголовку. private:методи ніколи не можуть користуватися цією вигодою.
підкреслюй_d
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.