Namespace + functies versus statische methoden op een klasse

Laten we zeggen dat ik een reeks gerelateerde functies heb of ga schrijven. Laten we zeggen dat ze met wiskunde te maken hebben. Moet ik organisatorisch:

  1. Schrijf deze functies en plaats ze in mijn MyMathnaamruimte en verwijs ernaar via MyMath::XYZ()
  2. Maak een klasse met de naam MyMathen maak deze methoden statisch en verwijs naar de vergelijkbare MyMath::XYZ()

Waarom zou ik de een boven de ander verkiezen om mijn software te organiseren?


Antwoord 1, autoriteit 100%

Gebruik standaard functies met naamruimte.

Klassen zijn om objecten te bouwen, niet om naamruimten te vervangen.

In objectgeoriënteerde code

Scott Meyers schreef een heel artikel voor zijn Effectieve C++-boek over dit onderwerp, “Prefereer niet-leden niet-vriendfuncties boven ledenfuncties”. Ik vond een online verwijzing naar dit principe in een artikel van Herb Sutter: http://www.gotw. ca/gotw/084.htm

Het belangrijkste om te weten is dat: In C++ behoren functies die zich in dezelfde naamruimte bevinden als een klasse, en die die klasse als parameter hebben, tot de interface van die klasse(omdat ADLzoekt die functies bij het oplossen van functieaanroepen).

Bijvoorbeeld:

  • laten we zeggen dat je een naamruimte N
  • . hebt

  • laten we zeggen dat je een klasse Chebt, gedeclareerd in naamruimte N(met andere woorden, de volledige naam is N::C)
  • laten we zeggen dat je een functie Fhebt, gedeclareerd in naamruimte N(met andere woorden, de volledige naam is N::F)
  • laten we zeggen dat de functie Fonder zijn parameters een parameter van het type C
  • heeft

… Dan maakt N::Fdeel uit van de openbare interface van N::C.

Functies met een naamruimte hebben, tenzij ze als “vriend” zijn aangegeven, geen toegang tot de interne onderdelen van de klasse, terwijl statische methoden dat wel hebben.

Dit betekent bijvoorbeeld dat bij het onderhouden van je klas, als je de interne onderdelen van je klas moet veranderen, je moet zoeken naar bijwerkingen in al zijn methoden, inclusief de statische.

Extensie I

Code toevoegen aan de interface van een klas.

In C# kun je methoden aan een klasse toevoegen, zelfs als je er geen toegang toe hebt. Maar in C++ is dit onmogelijk.

Maar, nog steeds in C++, kun je nog steeds een functie met namespaced toevoegen, zelfs aan een klasse die iemand voor je heeft geschreven.

Zie van de andere kant, dit is belangrijk bij het ontwerpen van uw code, want door uw functies in een naamruimte te plaatsen, machtigt u uw gebruikers om de interface van de klasse te vergroten/vervolledigen.

Extensie II

Een neveneffect van het vorige punt, het is onmogelijk om statische methoden in meerdere headers te declareren. Elke methode moet in dezelfde klasse worden gedeclareerd.

Voor naamruimten kunnen functies uit dezelfde naamruimte in meerdere headers worden gedeclareerd (de bijna standaard swap-functie is daar het beste voorbeeld van).

Extensie III

De coolheid van een naamruimte is dat je in sommige code de vermelding ervan kunt vermijden, als je het trefwoord usinggebruikt:

#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

En je kunt de “vervuiling” zelfs tot één klasse beperken:

#include <string>
#include <vector>
{
   using std::string ;
   string s ; // Ok
   vector v ; // COMPILATION ERROR
}
string ss ; // COMPILATION ERROR
vector vv ; // COMPILATION ERROR

Dit “patroon” is verplicht voor het juiste gebruik van het bijna standaard swap-idioom.

En dit is onmogelijk te doen met statische methoden in klassen.

Dus, C++-naamruimten hebben hun eigen semantiek.

Maar het gaat verder, omdat je naamruimten kunt combineren op een manier die vergelijkbaar is met overerving.

Als u bijvoorbeeld een naamruimte Amet een functie AAAheeft, een naamruimte Bmet een functie BBB, kunt u een naamruimte Cdeclareren en AAAen BBBin deze naamruimte plaatsen met het trefwoord using.

Je kunt zelfs de volledige inhoud van een naamruimte in een andere brengen, met using namespace, zoals getoond met naamruimte D!

namespace A
{
   void AAA();
   void AAA2();
}
namespace B
{
   void BBB();
}
namespace C
{
   using A::AAA;
   using B::BBB;
}
namespace D
{
   using namespace A;
   using namespace B;
}
void foo()
{
   C::AAA();
   // C::AAA2(); // ERROR, won't compile
   C::BBB();
}
void bar()
{
   D::AAA();
   D::AAA2();
   D::BBB();
}

Conclusie

Naamruimten zijn voor naamruimten.
Lessen zijn voor lessen.

C++ is zo ontworpen dat elk concept anders is en in verschillende gevallen anders wordt gebruikt als oplossing voor verschillende problemen.

Gebruik geen klassen als je naamruimten nodig hebt.

En in jouw geval heb je naamruimten nodig.


Antwoord 2, autoriteit 22%

Er zijn veel mensen die het niet met me eens zijn, maar dit is hoe ik het zie:

Een klasse is in wezen een definitie van een bepaald soort object. Statische methoden moeten bewerkingen definiëren die nauw verbonden zijn met die objectdefinitie.

Als je gewoon een groep gerelateerde functies wilt hebben die niet geassocieerd zijn met een onderliggend object of definitie van een soort object, dan zou ik zeggen dat je alleen met een naamruimte gaat. Alleen voor mij is dit conceptueel een stuk verstandiger.

In jouw geval vraag je jezelf bijvoorbeeld af: “Wat is een MyMath?” Als MyMathgeen soort object definieert, dan zou Ikzeggen: maak er geen klasse van.

Maar zoals ik al zei, ik weet dat er genoeg mensen zijn die het (zelfs fel) met me oneens zijn hierover (met name Java- en C#-ontwikkelaars).


Antwoord 3, autoriteit 7%

  • Als je statische gegevens nodig hebt, gebruik dan statische methoden.
  • Als het sjabloonfuncties zijn en u wilt een set sjabloonparameters voor alle functies samen kunnen specificeren, gebruik dan statische methoden in een sjabloonklasse.

Gebruik anders functies met naamruimte.


Als reactie op de opmerkingen: ja, statische methoden en statische gegevens worden vaak te veel gebruikt. Daarom heb ik slechts twee gerelateerdescenario’s aangeboden waarvan ik denk dat ze nuttig kunnen zijn. In het specifieke voorbeeld van de OP (een reeks wiskundige routines), als hij de mogelijkheid wilde hebben om parameters te specificeren – bijvoorbeeld een kerngegevenstype en uitvoerprecisie – die op alle routines zou worden toegepast, zou hij zoiets kunnen doen als:

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;

Als je dat niet nodig hebt, gebruik dan zekereen naamruimte.


Antwoord 4, autoriteit 4%

Je moet een naamruimte gebruiken, want een naamruimte heeft de vele voordelen ten opzichte van een klasse:

  • Je hoeft niet alles in dezelfde kop te definiëren
  • U hoeft niet al uw implementatie in de koptekst te tonen
  • Je kunt geen klaslid using; je kunt usingeen naamruimtelid
  • Je kunt geen using class, hoewel using namespaceniet zo vaak een goed idee is
  • Het gebruik van een klasse houdt in dat er een object moet worden gemaakt terwijl dat er in werkelijkheid niet is

Statische leden worden naar mijn mening heel erg veel gebruikt. Ze zijn in de meeste gevallen niet echt nodig. Functies van statische leden zijn waarschijnlijk beter af als functies voor bestandsbereik, en statische gegevensleden zijn slechts globale objecten met een betere, onverdiende reputatie.


Antwoord 5

Ik heb liever naamruimten, op die manier kun je privégegevens hebben in een anonieme naamruimte in het implementatiebestand (zodat het helemaal niet in de kop hoeft te verschijnen in tegenstelling tot private-leden ). Een ander voordeel is dat door usinguw naamruimte, de clients van de methoden zich kunnen afmelden voor het specificeren van MyMath::


Antwoord 6

Ik wil andere antwoorden samenvatten en toevoegen. Mijn perspectief is ook in de wereld van alleen kopteksten.


Naamruimten

Pluspunten:

  • eenvoudige oplossing voor het benoemen van hiërarchieën
  • ze bevatten geen semantiek, dus het is eenvoudiger te lezen
  • kan in verschillende bestanden (headers) leven
  • kan worden verlengd
  • ADL
  • snelkoppeling kan worden gedefinieerd (using).
  • Speelt goed met overbelasting van operators
  • Kan worden gebruikt voor branding (u kunt uw code ontwerpen en er zonder veel moeite een naamruimte overheen plaatsen)

Nadelen:

  • alles is openbaar
  • privé-dingen hebben een naamloze naamruimte nodig, dus het is niet expliciet
  • ADL (ja, sommige mensen verachten ADL)
  • kan worden uitgebreid (dit kan een slechte zaak zijn, vooral in combinatie met ADL, semantiek van bestaande code kan veranderen door de naamruimte uit te breiden)
  • functies moeten worden gedefinieerd (of gedeclareerd) in volgorde van gebruik

Klassen met statische methoden

Pluspunten:

  • kunnen privécomponenten hebben (functie, variabelen) en deze zijn expliciet gemarkeerd.
  • lessen kunnen worden bevriend
  • kan worden geparametriseerd (sjablonen)
  • kunnen zelf sjabloonparameters zijn
  • kan worden geïnstantieerd
  • kan worden doorgegeven aan functies (statische functies gedragen zich standaard als een niet-statische methode).
  • het is gemakkelijker om patronen te vinden en uit groepen van onafhankelijke functies te gaan en deze om te zetten in een echte klasse (eventueel met niet-statische leden)
  • afhankelijkheden tussen klassen zijn goed gedefinieerd
  • functies (de statische methode) kunnen in elke volgorde worden gedefinieerd

Nadelen:

  • Geen ADL
  • kan niet worden verlengd
  • heeft het zoekwoord static overal nodig (mogelijkheid om grappen te maken over de taal)
  • een overkill om alleen het naamgevingsprobleem op te lossen. In dat geval moeilijk leesbaar.
  • de functies (statische methoden) hebben altijd kwalificatie nodig (myclassspace::fun). Er is geen manier om snelkoppelingen te declareren (using).
  • bijna nutteloos voor overbelasting van de operator, daarvoor is een ingewikkeld vriendenmechanisme nodig.
  • kan niet worden gebruikt voor branding.
  • je moet onthouden en beëindig het met ;🙂

Samengevat, klassen met statische methoden zijn betere code-eenheden en laten meer meta-programmering toe, en met uitzondering van ADL en enkele syntactische eigenaardigheden, kunnen alle functies van naamruimten worden gerepliceerd, maar ze kunnen soms een overkill zijn.

Bedrijven, zoals Bloomberg, geven de voorkeur aan klassen boven naamruimten.
Als je niet van ADL of overbelasting door operators houdt, zijn lessen met statische methoden de juiste keuze.

IMO, het zou mooi zijn als naamruimte en klassen worden geïntegreerd om twee kanten van dezelfde medaille te worden.
Identificeer bijvoorbeeld een naamruimte in de taal als een klasse waar de methoden standaard statisch zijn.
En ze vervolgens als sjabloonparameters te kunnen gebruiken.
Ik zou niet weten wat ik met ADL moet doen (misschien kan het worden beperkt tot alleen symbolische operatorfuncties, bijv. operatorX, wat in de eerste plaats de oorspronkelijke motivatie was voor overbelasting van de operator en ADL)


Antwoord 7

Nog een reden om klasse te gebruiken – Optie om gebruik te maken van toegangsspecificaties. U kunt uw openbare statische methode dan eventueel opsplitsen in kleinere privémethoden. De openbare methode kan meerdere privémethoden aanroepen.


Antwoord 8

Zowel de naamruimte als de klassenmethode hebben hun nut. Naamruimten kunnen over bestanden worden verspreid, maar dat is een zwak punt als u alle gerelateerde code moet afdwingen om in één bestand te gaan. Zoals hierboven vermeld, kunt u met klasse ook privé-statische leden in de klas maken. Je kunt het in de anonieme naamruimte van het implementatiebestand hebben, maar het is nog steeds een groter bereik dan ze in de klasse te hebben.


Antwoord 9

Waarom zou ik de een boven de ander verkiezen om mijn software te organiseren?

Als je naamruimten gebruikt, zul je vaak een taalfout tegenkomen dat functies die elkaar aanroepen in een specifieke volgorde moeten worden vermeld, omdat C++ geen definities verderop in het bestand kan zien.

Als u klassen gebruikt, treedt dit defect niet op.

Het kan gemakkelijker en schoner zijn om implementatiefuncties in een klasse in te pakken dan om declaraties voor allemaal bij te houden of ze in een onnatuurlijke volgorde te plaatsen om het te compileren.

Other episodes