Где я должен поместить функции, которые не связаны с классом?


47

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

Где лучшее место для их размещения? Допустим, у меня есть это:

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

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

Какова хорошая практика в этой ситуации? Прямо сейчас я копирую их в новые классы, что, я уверен, является худшей практикой.


11
Вы узнали о staticключевом слове?
S.Lott

31
В C ++ свободные функции почти всегда предпочтительнее функций-членов.
Пабби

4
Нет правила, согласно которому все должно быть в классе. По крайней мере, не в C ++.
tdammers

2
Я бы предпочел пространство имен классу с кучей статических методов
Ник Кейли

Ответы:


71

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. Сезон по вкусу.
user253751

6

Зависит от того, как организован проект и какие шаблоны проектирования вы используете, при условии, что это чисто служебный код, у вас есть следующие варианты:

  • Если вам не нужно использовать объекты для всего, вы можете сделать что-то простое, например просто поместить их все в файл без обертки классов вокруг них. Это может быть с или без пространства имен, хотя пространство имен рекомендуется для предотвращения любых проблем в будущем.
  • Для управляемого C ++ вы можете создать статический класс, содержащий их все; однако, это на самом деле не работает так же, как реальный класс, и я понимаю, что это анти-паттерн C ++.
  • Если вы не используете управляемый C ++, вы можете просто использовать статические функции, чтобы позволить вам получить к ним доступ и хранить их все в одном классе. Это может быть полезно, если есть и другие функции, для которых вы бы хотели, чтобы объект был создан надлежащим образом, а также может быть анти-паттерном.
  • Если вы хотите обеспечить существование только одного экземпляра объекта, содержащего функции, вы можете использовать шаблон Singleton для служебного класса, который также дает вам некоторую гибкость в будущем, поскольку теперь у вас есть доступ к нестатическим атрибутам. Это будет ограниченное использование и действительно применимо, только если вам нужен объект по какой-то причине. Скорее всего, если вы сделаете это, вы уже будете знать, почему.

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


Почему отрицательный голос?
rjzii

10
Я не downvoter, но, вероятно, потому что вы советуете класс со статическими функциями или singleton, в то время как свободные функции, вероятно, будет хорошо в этом случае (и приемлемы и полезны для многих вещей в C ++).
Антон Голов

@AntonGolov - бесплатные функции - это первое, что я упомянул в списке. :) Остальные - это ООП-ориентированные подходы для ситуаций, когда вы имеете дело с «Все должно быть классом!» сред.
rjzii

9
@Rob Z: Тем не менее, C ++ не является одним из тех, "Все должно быть классом!" сред.
Дэвид Торнли

1
С каких это пор ООП принудительно вводить чистые функции в класс? Больше похоже на ООП-карго-культ.
Дедупликатор

1

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

Смотрите здесь для обсуждения статических функций-членов в нативном C ++ и здесь для статических классов в управляемом C ++. Затем вы можете использовать этот служебный класс, куда бы вы ни вставили свой код.

Например, в .NET такие вещи, как Min()и Max()предоставляются как статические члены System.Mathкласса .

Если все ваши функции связаны с математикой, и в противном случае у вас был бы гигантский Mathкласс, вы можете захотеть разбить его дальше и иметь такие классы, как TrigonometryUtilities, EucledianGeometryUtilitiesи так далее.

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


18
ИМХО, служебные классы, в которых нет ничего, кроме статических членов, являются антишаблоном в C ++. Вы используете класс, чтобы идеально воспроизвести поведение пространства имен, что на самом деле не имеет смысла.
ipeet

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

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();

чтобы быть более понятным.

C ++ STL имеет 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.