Де я повинен розмістити функції, не пов'язані з класом?


47

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

Де найкраще їх розмістити? Скажімо, у мене таке:

class A{
    public:
        int math_function1(int);
        ...
}

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

Яка хороша практика в цій ситуації? Зараз я копіював їх у нові класи, що, напевне, є найгіршою практикою.


11
Ви дізналися про staticключове слово?
S.Lott

30
У C ++ вільні функції майже завжди віддають перевагу функціям членів.
Паббі

4
Не існує правила, яке говорить, що все повинно бути в класі. Принаймні, не на C ++.
tdammers

2
Я вважаю за краще простір імен класу з купою статичних методів
Нік Кейлі

Відповіді:


70

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

namespace special_math_functions //optional
{
    int math_function1(int arg)
    {
         //definition 
    }
}

6
+1 Це найбільш розумне рішення, хоча додатковий простір імен не видається необхідним.
Паббі

1
ні, це не потрібно
jk.

27
Деякий простір імен корисний для зменшення можливих зіткнень імен з іншими бібліотеками.
Двері Білла

11
Використання простору імен також приємно, оскільки воно роз'єднує, чи є виклик методом чи функцією. ( math_function1(42)може викликати член поточного класу; special_math_functions::math_function1(42)чітко викликає незалежну функцію). Це, як було сказано, ::math_function(42)забезпечує таку ж розбірливість.
ipeet

2
Простори імен не потрібні, але вони також не заборонені. Тому ця відповідь говорить // optional. Приправити за смаком.
користувач253751

6

Залежить від того, як організовано проект та які типи моделей дизайну ви використовуєте, якщо припустити, що це строго корисний код, ви маєте такі варіанти:

  • Якщо вам не доведеться використовувати об’єкти для всього, ви могли б зробити щось просте, як просто помістити їх у файл без обгортки класу навколо них. Це може бути з простором імен або без нього, хоча простір імен рекомендується запобігати будь-яким проблемам у майбутньому.
  • Для керованого C ++ ви можете створити статичний клас, який містить їх усі; однак це насправді не працює так само, як фактичний клас, і я розумію, що це антидіапазон C ++.
  • Якщо ви не використовуєте керований C ++, ви можете просто скористатися статичними функціями, щоб дозволити вам отримати доступ до них і містити їх у одному класі. Це може бути корисно, якщо є також інші функції, для яких ви хочете, щоб належний екземплярний об'єкт був також може бути антидіаграмою.
  • Якщо ви хочете переконатися, що існуватиме лише один екземпляр об’єкта, що містить функції, тоді ви можете використовувати шаблон Singleton для класу утиліти, який також надає вам деяку гнучкість у майбутньому, оскільки тепер у вас є доступ до нестатичних атрибутів. Це буде обмеженим використанням і дійсно застосовується лише в тому випадку, якщо вам потрібен об'єкт з якихось причин. Шанси, якщо ви це зробите, ви вже будете знати, чому.

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


Чому голосування вниз?
rjzii

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

@AntonGolov - Безкоштовні функції - це перше, що я згадав у списку. :) Решта з них - це більш орієнтовані на ООП підходи для ситуацій, коли ти маєш справу з "Все повинно бути класом!" середовища.
rjzii

9
@Rob Z: Однак C ++ не з тих, "Все повинно бути класом!" середовища.
Девід Торнлі

1
З тих пір, коли OOP примушувати чисті функції в класі? Схоже, OOP-груз-культ.
Дедуплікатор

1

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

Дивіться тут для обговорення статичних функцій членів у рідному C ++ та тут для статичних класів у керованому C ++. Потім ви можете використовувати цей клас корисності, куди б ви не вставили свій код.

Наприклад, у .NET такі речі, як Min()і Max()надаються як статичні члени в System.Mathкласі .

Якщо всі функції математики , пов'язані , і ви б otherwiese мати гігантський Mathклас, ви можете розбити його далі , і є класи , як TrigonometryUtilities, EucledianGeometryUtilitiesі так далі.

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


18
IMHO, класи утиліти, які не мають нічого, крім статичних членів, є анти-шаблоном у C ++. Ви використовуєте клас, щоб ідеально відтворити поведінку простору імен, що насправді не має сенсу.
ipeet

+1 для згадування корисних класів. Такі мови, як C #, вимагають, щоб все було в класі, тому цілком звичайно створювати ряд корисних класів для різних цілей. Реалізація цих класів як Static робить утиліти ще більш зручними для користувачів і уникає головних болів, які спадщина може іноді створювати, особливо коли базові класи стають роздутими кодом, яким можуть користуватися лише один або два нащадки. Подібні методи можна застосовувати і в інших мовах, щоб забезпечити змістовний контекст для ваших функцій корисності, а не залишати їх плаваючими в глобальному масштабі.
S.Robins

5
@ S.Robins: У C ++ нічого подібного не потрібно, ви можете просто розмістити їх у просторі імен, що має точно такий же ефект.
DeadMG

0

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

// a general helper 
template <class T>
bool isPrinter(T& p){
   return (dynamic_cast<Printer>(p))? true: false;
}

    // specific helper for printers
namespace printer_utils {    
  namespace HP {
     print_alignment_page() { printAlignPage();}
  }

  namespace Xerox {
     print_alignment_page() { Alignment_Page_Print();}
  }

  namespace Canon {
     print_alignment_page() { AlignPage();}
  }

   namespace Kyocera {
     print_alignment_page() { Align(137,4);}
   }

   namespace Panasonic {
      print_alignment_page() { exec(0xFF03); }
   }
} //namespace

Тепер isPrinterдоступний будь-який код, включаючи його заголовок, але print_alignment_pageвимагає using namespace printer_utils::Xerox;директиви. Можна також згадати це як

Canon::print_alignment_page();

бути більш чітким.

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

Насправді НЕ рекомендується використовувати using namespace std;у файлі заголовка або, як це часто робиться, у якості першого рядка всередині main(). std::- це 5 букв, і часто здається, що заздалегідь є передмовою функція, яку хочеться використовувати (особливо std::coutі std::endl!), але вона служить цілі.

У новому C ++ 11 є кілька просторів під імен для спеціальних служб, таких як

std::placeholders,
std::string_literals,
std::chrono,
std::this_thread,
std::regex_constants

які можна принести для використання.

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

#include <iostream>
#include <string>
#include <vector>

namespace Needed {
  using std::vector;
  using std::string;
  using std::cout;
  using std::endl;
}

int main(int argc, char* argv[])
{
  /*  using namespace std; */
      // would avoid all these individual using clauses,
      // but this way only these are included in the global
      // namespace.

 using namespace Needed;  // pulls in the composition

 vector<string> str_vec;

 string s("Now I have the namespace(s) I need,");

 string t("But not the ones I don't.");

 str_vec.push_back(s);
 str_vec.push_back(t);

 cout << s << "\n" << t << endl;
 // ...

Ця методика обмежує експозицію в цілому std:: namespace( вона велика! ) І дозволяє писати чистіший код для найпоширеніших рядків коду, які люди пишуть найчастіше.


-2

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

template <typename T>
T math_function1(T){
 ..
}

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

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