Przestrzeń nazw + funkcje kontra metody statyczne w klasie

290

Powiedzmy, że mam lub zamierzam napisać zestaw powiązanych funkcji. Powiedzmy, że są związane z matematyką. Pod względem organizacyjnym powinienem:

  1. Napisz te funkcje i umieść je w mojej MyMathprzestrzeni nazw i odwołuj się do nich za pośrednictwemMyMath::XYZ()
  2. Utwórz klasę o nazwie MyMathi ustaw te metody statycznie i odwołuj się do nich podobnieMyMath::XYZ()

Dlaczego miałbym wybierać między sobą jako sposób organizacji mojego oprogramowania?

RobertL
źródło
4
po pierwsze, przestrzenie nazw są nowszym dodatkiem do języka, w porównaniu do klas i metod statycznych, które były w języku od czasu, gdy nazywał się „C z klasami”. Niektórzy programiści mogą czuć się bardziej komfortowo ze starszymi funkcjami. Niektórzy inni programiści mogą używać starych kompilatorów. Tylko moje 0,02 $
Rzym,
21
@Rom: Masz rację co do „starych programistów”, ale mylisz się co do „starych kompilatorów”. Przestrzenie nazw są poprawnie kompilowane od eonów (pracowałem z nimi z Visual C ++ 6, datowanym na 1998 rok!). Jeśli chodzi o „C z klasami”, niektórzy ludzie na tym forum nawet się nie urodzili, gdy tak się stało: używanie tego jako argumentu, aby uniknąć standardowej i powszechnej funkcji C ++, jest błędem. Podsumowując, tylko przestarzałe kompilatory C ++ nie obsługują przestrzeni nazw. Nie używaj tego argumentu jako wymówki, aby go nie używać.
paercebal,
@paercebal: niektóre starożytne kompilatory są nadal używane w świecie osadzonym. Nieobsługiwanie przestrzeni nazw jest prawdopodobnie jedną z najmniejszych niedogodności, które należy znosić podczas pisania kodu dla różnych małych procesorów, z którymi każdy wchodzi w interakcję na co dzień: stereo, mikrofalówka, jednostka sterująca silnika w samochodzie, sygnalizacja świetlna itp. bądźmy szczerzy: nie opowiadam się za używaniem lepszych, nowszych kompilatorów na całym świecie. Au conrare: Jestem za najnowszymi funkcjami językowymi (oprócz RTTI;)). Po prostu wskazuję, że taka tendencja istnieje
Rzym.
13
@Rom: W obecnym przypadku autor pytania ma wybór, więc najwyraźniej żaden z jego kompilatorów nie skompilował kodu z przestrzenią nazw. Ponieważ jest to pytanie dotyczące C ++, należy podać odpowiedź C ++, w tym w razie potrzeby wspomnieć o przestrzeniach nazw i rozwiązaniach RTTI problemu. Udzielenie odpowiedzi w języku C lub odpowiedzi w języku C z klasami dla przestarzałych kompilatorów nie jest tematem.
paercebal 21.01.11
2
„Just my $ .02” - byłoby więcej warte, gdybyś dostarczył dowód istnienia istniejących kompilatorów C ++, które nie obsługują przestrzeni nazw. „niektóre starożytne kompilatory są nadal używane w świecie osadzonym” - to głupie; „świat osadzony” jest nowszym rozwiązaniem niż przestrzenie nazw C ++. „C z klasami” zostało wprowadzone w 1979 r., Na długo zanim ktokolwiek osadził kod C w czymkolwiek. „Po prostu wskazuję, że taka tendencja istnieje” - nawet jeśli tak, to nie ma to nic wspólnego z tym pytaniem.
Jim Balter

Odpowiedzi:

243

Domyślnie używaj funkcji z przestrzenią nazw.

Klasy mają budować obiekty, a nie zastępować przestrzenie nazw.

W kodzie obiektowym

Scott Meyers napisał cały artykuł do swojej książki Effective C ++ na ten temat: „Wolę funkcje nieprzyjazne od innych niż funkcje członkowskie”. Internetowe odniesienie do tej zasady znalazłem w artykule z Herb Sutter:http://www.gotw.ca/gotw/084.htm

Ważne jest, aby wiedzieć, że: W C ++ funkcje w tej samej przestrzeni nazw, w której klasa należy do interfejsu tej klasy (ponieważ ADL przeszuka te funkcje podczas rozwiązywania wywołań funkcji).

Funkcje z przestrzenią nazw, chyba że zostaną uznane za „przyjaciel”, nie mają dostępu do wewnętrznych elementów klasy, podczas gdy metody statyczne mają.

Oznacza to na przykład, że utrzymując klasę, jeśli chcesz zmienić elementy wewnętrzne swojej klasy, będziesz musiał szukać efektów ubocznych we wszystkich jej metodach, w tym statycznych.

Rozszerzenie I

Dodawanie kodu do interfejsu klasy.

W języku C # możesz dodawać metody do klasy, nawet jeśli nie masz do niej dostępu. Ale w C ++ jest to niemożliwe.

Ale nadal w C ++ możesz nadal dodawać funkcje przestrzeni nazw, nawet do klasy, którą ktoś dla ciebie napisał.

Patrz z drugiej strony, jest to ważne przy projektowaniu kodu, ponieważ umieszczając swoje funkcje w przestrzeni nazw, upoważnisz użytkowników do zwiększenia / ukończenia interfejsu klasy.

Rozszerzenie II

Efektem ubocznym poprzedniego punktu jest niemożliwość zadeklarowania metod statycznych w wielu nagłówkach. Każda metoda musi być zadeklarowana w tej samej klasie.

W przypadku przestrzeni nazw funkcje z tej samej przestrzeni nazw można zadeklarować w wielu nagłówkach (prawie standardowa funkcja zamiany jest tego najlepszym przykładem).

Rozszerzenie III

Podstawową zaletą przestrzeni nazw jest to, że w niektórych kodach można uniknąć wspominania o niej, jeśli użyjesz słowa kluczowego „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

Możesz nawet ograniczyć „zanieczyszczenie” do jednej klasy:

#include <string>
#include <vector>

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

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

Ten „wzorzec” jest obowiązkowy dla właściwego użycia prawie standardowego idiomu zamiany.

Jest to niemożliwe w przypadku metod statycznych w klasach.

Przestrzenie nazw C ++ mają więc własną semantykę.

Ale idzie dalej, ponieważ można łączyć przestrzenie nazw w sposób podobny do dziedziczenia.

Na przykład, jeśli masz przestrzeń nazw A z funkcją AAA, przestrzeń nazw B z funkcją BBB, możesz zadeklarować przestrzeń nazw C i wprowadzić AAA i BBB w tej przestrzeni nazw za pomocą słowa kluczowego.

Wniosek

Przestrzenie nazw dotyczą przestrzeni nazw. Klasy są dla klas.

C ++ został zaprojektowany, więc każda koncepcja jest inna i jest używana w różnych przypadkach, w różnych przypadkach, jako rozwiązanie różnych problemów.

Nie używaj klas, gdy potrzebujesz przestrzeni nazw.

A w twoim przypadku potrzebujesz przestrzeni nazw.

paercebal
źródło
Czy tę odpowiedź można zastosować także do wątków, tj. Czy lepiej jest używać przestrzeni nazw zamiast statycznych metod wątków?
dashy
3
@dashesy: przestrzenie nazw vs. metody statyczne nie mają nic wspólnego z wątkami, więc tak, przestrzenie nazw są lepsze, ponieważ przestrzenie nazw są prawie zawsze lepsze niż metody statyczne. Jeśli jedno, metody statyczne mają dostęp do zmiennych członków klasy, więc mają one jakoś niższą wartość enkapsulacji niż przestrzenie nazw. Izolowanie danych jest jeszcze ważniejsze w przypadku wykonywania wątków.
paercebal
@ paercebal- dzięki, korzystałem z metod klasy statycznej dla funkcji wątku. Teraz rozumiem, że niewłaściwie wykorzystałem klasę jako przestrzeń nazw, więc jakie według Ciebie jest najlepsze podejście do posiadania wielu wątków w jednym obiekcie? Zadałem to pytanie również w SO, doceniam, czy możesz rzucić nieco światła (tutaj lub w samym pytaniu)
dashy
1
@dashesy: ​​Pytasz o kłopoty. W przypadku różnych wątków chcesz izolować dane, które nie powinny być udostępniane, więc posiadanie wielu wątków mających uprzywilejowany dostęp do prywatnych danych klasy jest złym pomysłem. Chciałbym ukryć jeden wątek w klasie i upewnić się, że dane dla tego wątku są izolowane od danych dla głównego wątku. Oczywiście dane, które mają być udostępnione, mogą należeć do tej klasy, ale nadal powinny być zsynchronizowane (blokady, atomowe itp.). Nie jestem pewien, do ilu bibliotek masz dostęp, ale korzystanie z zadań / asynchronizacji jest jeszcze lepsze.
paercebal
odpowiedź paercebala powinna być zaakceptowana! Jeszcze tylko jeden link do prawie standardowej wymiany () poprzez przestrzeń nazw + ADL -> stackoverflow.com/questions/6380862/…
Gob00st
54

Jest wielu ludzi, którzy nie zgadzają się ze mną, ale tak to widzę:

Klasa jest zasadniczo definicją pewnego rodzaju obiektu. Metody statyczne powinny definiować operacje ściśle związane z definicją obiektu.

Jeśli chcesz mieć grupę powiązanych funkcji niezwiązanych z obiektem bazowym lub definicją rodzaju obiektu , powiedziałbym, że idź z tylko przestrzenią nazw. Dla mnie, koncepcyjnie, jest to o wiele bardziej sensowne.

Na przykład w twoim przypadku zadaj sobie pytanie: „Co to jest MyMath?” Jeśli MyMathnie określa rodzaj obiektu, a ja powiedziałbym: nie sprawiają, że klasa.

Ale tak jak powiedziałem, wiem, że jest wielu ludzi, którzy (nawet gwałtownie) nie zgadzają się ze mną w tej sprawie (w szczególności deweloperzy Java i C #).

Dan Tao
źródło
3
Masz na to bardzo czyste spojrzenie. Ale praktycznie mówiąc, przydatna może być klasa z całkowicie statycznymi metodami: możesz typedefje, użyć ich jako parametrów szablonów itp.
Shog9 16.09.2009
56
To dlatego, że ludzie Jave i C # nie mają wyboru.
Martin York,
7
@ shog9. Możesz także szablonować funkcje!
Martin York,
6
@ Dan: przypuszczalnie taki, który potrzebował procedur matematycznych i chciał obsługiwać „podłączanie” różnych implementacji.
Shog9,
1
@Dan: „Myślę, że jeśli ktoś jest zainteresowany wykorzystaniem klasy jako parametru szablonu, klasa ta prawie na pewno definiuje jakiś podstawowy obiekt”. Nie, wcale nie. Pomyśl o cechach. (Niemniej jednak w pełni zgadzam się z twoją odpowiedzią.)
sbi
18
  • Jeśli potrzebujesz danych statycznych, użyj metod statycznych.
  • Jeśli są to funkcje szablonów i chcesz mieć możliwość określenia zestawu parametrów szablonu dla wszystkich funkcji razem, użyj metod statycznych w klasie szablonów.

W przeciwnym razie użyj funkcji z przestrzenią nazw.


W odpowiedzi na komentarze: tak, metody statyczne i dane statyczne są zwykle nadmiernie wykorzystywane. Dlatego zaproponowałem tylko dwa powiązane scenariusze, w których moim zdaniem mogą być pomocne. W konkretnym przykładzie PO (zestaw procedur matematycznych), jeśli chciałby możliwości określenia parametrów - powiedzmy, podstawowego typu danych i precyzji wyjściowej - które miałyby zastosowanie do wszystkich procedur, mógłby zrobić coś takiego:

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;

Jeśli nie musisz, wtedy przez wszystkich środków używać nazw.

Shog9
źródło
2
tak zwane dane statyczne mogą być danymi na poziomie przestrzeni nazw w pliku implementacyjnym przestrzeni nazw, co jeszcze bardziej zmniejsza sprzężenie, ponieważ nie musi pojawiać się w nagłówku.
Motti
Dane statyczne nie są lepsze niż globale o zasięgu nazw.
coppro
@coppro. Są co najmniej o jeden krok w górę łańcucha ewolucyjnego od przypadkowych globali, ponieważ można je uczynić prywatnymi (ale inaczej się zgadzają).
Martin York,
@Motti: OTOH, jeśli chcesz go w nagłówku (funkcje wstawiania / szablonu), wrócisz do brzydoty .
Shog9
1
Interesujący przykład, w którym klasa służy jako skrót, aby uniknąć powtarzania templateargumentów!
underscore_d
13

Powinieneś użyć przestrzeni nazw, ponieważ przestrzeń nazw ma wiele zalet w stosunku do klasy:

  • Nie musisz definiować wszystkiego w tym samym nagłówku
  • Nie musisz ujawniać całej swojej implementacji w nagłówku
  • Nie możesz usingczłonka klasy; możesz być usingczłonkiem przestrzeni nazw
  • Nie możesz using class, choć using namespacenie jest to często dobry pomysł
  • Korzystanie z klasy oznacza, że ​​istnieje jakiś obiekt do utworzenia, gdy tak naprawdę go nie ma

Moim zdaniem członkowie statyczni są bardzo nadużywani. W większości przypadków nie są one koniecznością. Funkcje elementów statycznych są prawdopodobnie lepsze niż funkcje zakresu plików, a elementy danych statycznych to tylko obiekty globalne o lepszej, niezasłużonej reputacji.

coppro
źródło
3
„Nie musisz ujawniać całej swojej implementacji w nagłówku”, podobnie jak podczas korzystania z klasy.
Vanuan
Co więcej: jeśli używasz przestrzeni nazw, nie możesz ujawnić całej swojej implementacji w nagłówku (skończysz na wielu definicjach symboli). Wbudowane funkcje członka klasy pozwalają to zrobić.
Vanuan
1
@ Vanuan: W nagłówku możesz ujawnić implementacje przestrzeni nazw. Wystarczy użyć inlinesłowa kluczowego, aby spełnić wymagania ODR.
Thomas Eding,
@ThomasEding nie potrzebuje! = Może
Vanuan
3
@ Vanuan: Kompilator gwarantuje tylko jedną rzecz inline, a NIE jest to „wstawianie” treści funkcji. Rzeczywistym (i gwarantowane przez standard) Celem inlinejest, aby uniknąć tworzenia definicji. Przeczytaj o „Zasadzie jednej definicji” dla C ++. Ponadto połączone pytanie SO nie zostało skompilowane z powodu problemów ze wstępnie skompilowanym nagłówkiem zamiast problemów z ODR.
Thomas Eding,
3

Wolałbym przestrzenie nazw, w ten sposób możesz mieć prywatne dane w anonimowej przestrzeni nazw w pliku implementacji (więc nie musi wcale pojawiać się w nagłówku, w przeciwieństwie do privateczłonków). Kolejną korzyścią jest to, że dzięki usingprzestrzeni nazw klienci metod mogą zrezygnować z określaniaMyMath::

Motti
źródło
2
Możesz również przechowywać prywatne dane w anonimowej przestrzeni nazw w pliku implementacji z klasami. Nie jestem pewien, czy podążam za twoją logiką.
Patrick Johnmeyer,
0

Jeszcze jeden powód, aby używać klasy - Opcja korzystania ze specyfikatorów dostępu. Następnie możesz ewentualnie podzielić swoją publiczną metodę statyczną na mniejsze metody prywatne. Metoda publiczna może wywoływać wiele metod prywatnych.

Milind T.
źródło
6
Modyfikatory dostępu są fajne, ale nawet najbardziej private metoda jest bardziej dostępna niż metoda, której prototyp nie jest w ogóle opublikowany w nagłówku (a zatem pozostaje niewidoczny). Nie wspominam nawet o lepszej enkapsulacji oferowanej przez anonimowe funkcje przestrzeni nazw.
paercebal
2
Metody prywatne są, IMO, gorsze od ukrywania samej funkcji w implementacji (plik cpp) i nigdy nie ujawniają jej w pliku nagłówkowym. Proszę wyjaśnić to w swojej odpowiedzi i dlaczego wolisz korzystać z prywatnych członków. Do tego czasu -1.
nonsensickle
@nonsensickle Być może ma na myśli, że mamutowa funkcja z wieloma powtarzającymi się sekcjami może zostać bezpiecznie rozbita podczas ukrywania obrażających podsekcji za prywatnymi, uniemożliwiając innym dostęp do nich, jeśli są niebezpieczne / wymagają bardzo ostrożnego użycia.
Troyseph,
1
@Troyseph, mimo to, możesz ukryć te informacje w nienazwanej przestrzeni nazw w .cpppliku, co uczyniłoby to prywatnym dla tej jednostki tłumaczeniowej bez podawania zbędnych informacji osobom czytającym plik nagłówkowy. Skutecznie staram się opowiadać za idiomem PIMPL.
nonsensickle
Nie możesz umieścić go w .cpppliku, jeśli chcesz używać szablonów.
Yay295,
0

Zarówno przestrzeń nazw, jak i metoda klasy mają swoje zastosowania. Przestrzeń nazw może być rozłożona na pliki, ale jest to słabość, jeśli trzeba wymusić cały powiązany kod, aby przejść do jednego pliku. Jak wspomniano powyżej klasa pozwala również tworzyć prywatne statyczne elementy w klasie. Możesz mieć go w anonimowej przestrzeni nazw pliku implementacji, jednak nadal jest to większy zakres niż posiadanie ich w klasie.

rocd
źródło
„[przechowywanie rzeczy] w anonimowej przestrzeni nazw pliku implementacji [jest] większym zasięgiem niż umieszczenie ich w klasie” - nie, nie jest. w przypadkach, w których uprzywilejowany dostęp do członków nie jest wymagany, anonimowe obiekty o nazwach są bardziej prywatne niż private:te. a w wielu przypadkach, gdy wydaje się, że wymagany jest uprzywilejowany dostęp , można to uwzględnić. najbardziej „prywatna” funkcja to taka, która nie pojawia się w nagłówku. private:metody nigdy nie mogą korzystać z tej korzyści.
underscore_d