Hvor mye funksjonalitet er "akseptabelt" for en C ++ struct?

stemmer
16

Mitt første innlegg så kan du gå lett på meg!

Jeg vet at det er ingen reell forskjell mellom structs og klasser i C ++, men mange mennesker, inkludert meg bruke en struct eller klasse vise innstilt - structs for gruppering ren gamle data og klasser for innkapslet data som har meningsfylte operasjoner.

Nå er det helt greit, men på hvilket punkt du begynner å tenke at noe er ikke bare en struct lenger, og bør bli en klasse?

Ting jeg synes er rimelig for structs å ha:

  1. konstruktører med enkel initialisering kode bare.
  2. serialisering kode, for eksempel bekke innsetting / ekstraksjons operatører.

Ting jeg er ikke så sikker på om, men vil trolig gjøre:

  1. sammenligningsoperatorer
  2. Enkle transformasjon funksjoner - for eksempel byteswapping alle medlemmer etter mottak av data fra en ekstern kilde.

Jeg tror ikke structs bør ha:

  1. dynamisk minnetildeling.
  2. destructor.
  3. komplekse medlemsfunksjoner.

Hvor grensene ligge ???

Dessuten er det rimelig å ha klasse tilfeller som medlemmer av en struct? f.eks

class C {private: int hiddenData; public: void DoSomething();};

struct S {int a; float b; C c; };

S s; s.c.DoSomething();

Husk, jeg er ikke videre om hva du kan gjøre med C ++, er jeg interessert i hva du bør gjøre når du utformer god programvare.

Tanker?

Publisert på 17/03/2009 klokken 14:59
kilden bruker
På andre språk...                            


19 svar

stemmer
13

Jeg tror det er tre store, sammenhengende skoler tanke:

  1. Folk som ikke bryr seg uansett og bruke structog classom hverandre.
  2. Folk som bruker structeneste til å representere liten POD.
  3. Folk som bruker structs som poster.

Jeg kan ikke gjøre en avgjørende argument for noen av disse strategiene. Jeg har en tendens til å følge banen 2, men jeg også bruke structs for ikke-POD typer når jeg ser det på sin plass, særlig for funksjons gjenstander (selv om disse ikke kan oppfylle POD-krav).

(Forresten, har C ++ FAQ lite en ganske god definisjon av POD ).

EDIT jeg ikke røre mal metaprogramming teknikker, som å bruke structfor plassholdere (type tags) eller for å implementere metafunctions. Jeg antar det er absolutt ingen uenighet i disse tilfellene: siden de aldri inneholde metoder (eller data), bruk alltid struct).

Svarte 17/03/2009 kl. 15:07
kilden bruker

stemmer
10

Min personlige preferanse er å kun bruke structs hvis det er ingen metoder i det hele tatt. Hvis jeg trenger å legge til en metode eller annen grunn, er det en klasse.

Svarte 17/03/2009 kl. 15:05
kilden bruker

stemmer
9

Klasse vs. struct

Ved hjelp av klasse eller struct søkeordet er et spørsmål om smak sammen med ' følelse ' den produserer på leseren. Teknisk sett er de tilsvarende, men lesbarheten er bedre hvis structs brukes for PODs og C-struct typer og klasser for noe annet.

Grunnleggende ting som bør gå i en C ++ struct: Konstruktør som initialiserer data (jeg misliker å bruke memset, og det kan senere bite tilbake dersom POD utvikler seg til noe annet) eller bygging av andre typer, men ikke kopiere konstruktør.

Hvis du trenger å definere en kopi konstruktør eller oppdrag operatør fordi kompilatoren genererte ikke er god nok, gjør det til en klasse.

Det er vanlig å bruke structs også for funktorer som skal sendes til STL algoritmer og mal metaprogramming, som i

struct square_int {
   int operator()( int value )
   {
      return value*value;
   }
};
std::transform( v.begin(), v.end(), v.begin(), square_int() );

eller

// off the top of my head
template <typename T>
struct is_pointer { enum { value = false } };

template <typename T>
struct is_pointer<T*> { enum { value = true } };

Medlems metoder kontra gratis funksjoner

Foruten det jeg har sagt før, at ikke legge til hva andre allerede har svart, jeg ønsket å sette litt fokus på andre typer funksjoner som du kommentere i innlegget ditt, som sammenligningsoperatorer og lignende.

Operatører som er ment å være symmetrisk (sammenligning, aritmetikk), er innsetting og sletting operatører og transformasjoner som regel bedre implementert som gratis funksjoner uavhengig av om du erklære det som en klasse eller struct.

Symmetriske operatorer (med hensyn til datatyper) er ikke symmetrisk om de er utført som medlemsfunksjoner. Oppslags reglene vil ikke kaste på venstre side for å ringe et medlem funksjon, men det vil gjelde de samme cast å matche en gratis funksjon.

   // Example of symmetry with free functions where method would be asymmetric
   int main()
   {
      std::string( "Hello " ) + "world"; // compiles as free / member function
      "Hello " + std::string( "world" ); // compiles with free function, fails with member function definition of +
   }

I koden ovenfor, hvis operatøren + var et medlem metode for std :: string kompilatoren ikke ville klare å lage som det ikke kan kaste const char * bokstavelig inn i en std :: string å bruke medlem metoden.

Innsetting og uttak fra strømmene må alltid være implementert som frie funksjoner som strømmen er alltid den venstre side av operasjonen.

Holde transformasjoner som gratis funksjoner isolerer de to forskjellige typer. Hvis A og A 'kan konverteres til en annen, og du bestemmer deg for å gjennomføre transformasjoner som medlemmer av en, så en må vite A' og all bruk av A vil avhenge av A' om du bruker det eller ikke. Hvis du definerer transformasjon som en gratis funksjon, er et komplett uten A' og koblingen mellom de to klassene / structs vil bli mindre. Det samme gjelder for transformasjoner til / fra nettverk, serialisering og deserialization. Når du implementerer dem i klassen / konstruere du tvinger alle brukerne å vite om disse forvandler.

Svarte 17/03/2009 kl. 16:19
kilden bruker

stemmer
3

Du kan se på hva Standard Library gjør. Alles favoritt struct std :: paret har bare konstruktører.

Jeg synes bruken av konstruktører med structs så praktisk og naturlig at jeg ikke kan tenke meg å gjøre uten dem. Jeg gir aldri structs andre metoder, men selvfølgelig kan det være gratis funksjoner eller medlemmer av andre klasser som tar dem som parametre.

Svarte 17/03/2009 kl. 16:28
kilden bruker

stemmer
3

Jeg bruker klasser og innkapsling når jeg for å opprettholde invariantene og dataintegritet. Hvis du ikke har noen invarianter, og dataene er egentlig bare en bøtte med elementer, det har alltid vært bra i vår butikk for å bruke struct selv om du legger fancy hjelpe konstruktører eller funksjoner. Men jo mer du gjør dekorere det, bør det gjør du stoppe og tenke at det kanskje bør være en klasse.

Hvis du trenger å være POD kompatibel (for eksempel grensesnitt til C-kode) du fortsatt trenger å bruke struct. Men du kan bryte denne struct i en klasse og avsløre det med en get () -funksjonen for grensesnitt med C API. Eller lage en hjelper funksjonen til å returnere en skikkelig POD struct fra klassen.

Svarte 17/03/2009 kl. 15:33
kilden bruker

stemmer
3

Fra å lese gjennom noen STL kildekoden som leveres med Visual Studio, virker det som et kriterie i bruk er det ting "stort sett public" (start med en struct ) eller "hovedsakelig privat" (start med en klasse )?

På samme måte, hvis det du skriver øverst (kanskje fordi det er viktig) i klassen er offentlig , og deretter gå med struct . På den annen side, hvis du liste medlemsdata første bruk klasse .

Svarte 17/03/2009 kl. 15:12
kilden bruker

stemmer
2

Jeg kan være i mindretall, men jeg bruker structer å bety én ting, "rekkefølgen av biter saker". Noe som må serialisert på disk eller nettverk, eller må være kompatibel med noen tredjepart bibliotek og må være i riktig rekkefølge, eller om det er å gjøre noen form for bit felt magi som en prosessor spesifikk optimalisering, som alltid går inn i en struct. Omorganisere feltene i en struct, derfor har alltid en slags konsekvenser og bør være nøye gjennomtenkt.

classes har imidlertid felt som bare er semantisk meningsfylt. Hvis for noen grunn jeg eller noen andre ønsker å omorganisere eller endre data medlemmer av en class, kan dette skje ganske fritt.

Svarte 17/03/2009 kl. 18:29
kilden bruker

stemmer
2

Jeg vil legge til noen svært få metoder til en struct, som et spørsmål om bekvemmelighet, bare hvis jeg faktisk bruker dem. Selvfølgelig alle av dem er offentlige. Hvis jeg trenger noe mer enn dette, det umiddelbart blir konvertert til en klasse.

  1. En standard konstruktør, for å initialisere data medlemmer til kjente verdier.
  2. En konstruktør med en parameter for hvert medlem.
  3. operatør <, for enkel inkludering i sett og kart.
  4. Operatøren ==.
Svarte 17/03/2009 kl. 16:38
kilden bruker

stemmer
1

mange mennesker, inkludert meg bruke en struct eller klasse vise innstilt - structs for gruppering "ren gamle data" og klasser for innkapslet data som har meningsfylte operasjoner.

IMHO, er dette differensiering en misforståelse, men det er godt forstått hvorfor det brukes sånn. Den er basert på tradisjonelle konvensjoner og innebygd følelse om klasse vs struct. Jeg vil følge Marshall Cline forslag:

7.8 Hva er forskjellen mellom søkeordene struct og klasse?

Siden det er betydningen de fleste allerede har, bør du sannsynligvis bruke struct søkeord hvis du har en klasse som har svært få metoder og har offentlige data (slike ting finnes i godt utviklet systemer!), Men ellers bør du nok bruke klassen søkeord.

Personlig har jeg (igjen) bruke structsøkeord formetafunctions

Svarte 04/02/2010 kl. 00:59
kilden bruker

stemmer
1

Konsistens er viktigst. Poenget med konvensjonene er å gi et felles referansepunkt for alle de som leser koden din i fremtiden.

Personlig unngår jeg structs hvis jeg føler jeg trenger funksjonaliteten til en klasse.
AKA: "ren gamle data" tilnærming.

Se på prosjektet og sette en standard, eller forholde seg til en allerede er der.

Skjønt, foreslår jeg at du prøver å unngå inheritence, spesielt hvis alle struct gjør holder POD. Ingenting er verre deretter å spore en haug med super klasser for et heltall eller røye.

Svarte 17/03/2009 kl. 15:10
kilden bruker

stemmer
0

Jeg var ikke å gjøre et skille mellom medlem og gratis funksjoner når jeg tenkte på det spørsmålet, men jeg ser nå at dette var en feil. Det ser nå ut til meg at structs bør bare sjelden har medlemsfunksjoner. Det er ganske klart at alt i en struct bør være offentlig.

Derfor er det i alminnelighet ingen vits i å ha struct medlemsfunksjoner fordi en hvilken som helst funksjon kan endre dataene i struct. Et medlem funksjon på en struct ville være et praktisk snarere enn en nødvendighet.

De eneste unntakene vil være ting som kreves for å være medlem funksjoner for noen andre formål - konstruktører for initial arrays; sammenligninger for bruk med et kart, ting som brukes av maler; etc.

Kanskje klassen og struct bør bli sett på som motpoler - en klasse eksponerer funksjoner og skjuler data, mens en struct eksponerer data og lar deg skjule funksjoner.

Gå tilbake til byte bytte eksempel:

struct S
{
    int data;
    S() {data = 1234;}
    void ByteSwap() {network::ByteSwap( &data );}
};

S s;
s.ByteSwap();

ville bli:

struct S
{
    S() {data = 1234;}
    int data;
};

namespace network
{
    void ByteSwap( int* i ) {/*byte swap *i*/}
    void ByteSwap( S* s ) {ByteSwap( &s->data );}

    S s;
    ByteSwap(&s);
}

Dette er fornuftig når dataene og enkelte funksjoner ikke alltid sterkt hører sammen. Byte bytte ville bare være av interesse for nettverket systemet, men høyere nivå funksjoner kan fortsatt bruke struct uten selv å vite om lavt nivå ting som byte bytte.

En annen fordel i denne saken er at de relaterte byte swapping operasjoner er alle holdt sammen på samme sted.

Svarte 24/03/2009 kl. 21:27
kilden bruker

stemmer
0

Min enkel tommelfingerregel for structs og klasser:

if (data_requires_strict_alignment == TRUE) {
  use(struct);
} else {
  use(class);
}

Det vil si, hvis data du representerer tilsvarer noen dataobjekt som har strenge medlem orden og krav til innretning (for eksempel en datastruktur utveksles med maskinvaren på drivernivå), bruker et struct. For alle andre tilfeller, bruk en klasse. Klassene har så mange funksjoner og muligheter som structs ikke, og i mine erfaringer er det fordelaktig å bruke klasser når det er mulig, selv om du ikke bruker noen av de ekstra funksjonene i øyeblikket (om ikke annet, er en data-klasse som en struct med et tryggere tilgangsnivå som standard). Reserve structs i de tilfellene der du trenger de unike egenskapene til en struct, nemlig muligheten til å spesifisere lagselementer i en spesiell rekkefølge og med nøyaktig innretning / polstring (typisk med lav-nivå kommunikasjon, f.eks drivere), slik at man kan kaste det til en byte array, brukes memcpy()på den, etc. Hvis man følger denne modell, da en klasse ville ikke bli brukt som et medlem av en struktur (siden en klassedefinisjon ikke angir oppstilling eller en forutsigbar verdi for sizeof(class)). Likeledes, hvis du tenker på konstruktører eller operatører, bruk en klasse.

Denne tommelfingerregelen bidrar også gjøre det enklere å samhandle med C-kode, siden structs blir brukt på en måte som er forenlig med C-strukturer.

Svarte 18/03/2009 kl. 16:40
kilden bruker

stemmer
0

Vanligvis bruker jeg classnår jeg trenger å bruke tilgangsbransjen. Dette har den effekten at de fleste av mine topp-nivå ting forbli som klasser, mens de sjeldne POD samlinger og mine ikke-så-sjeldne indre klasse pimpls er vanligvis structs.

Svarte 18/03/2009 kl. 15:40
kilden bruker

stemmer
0

Jeg bruker struct når jeg ønsker å bruke data medlem som grensesnitt av objektet. Jeg vil endre struct til en klasse, når det er behov for å legge til en privat del.

Svarte 17/03/2009 kl. 21:58
kilden bruker

stemmer
0

Jeg bruker alltid structs for 'klumper av data', selv om de blir dekorert med en konstruktør og noen ganger sammenligning operatører, de får aldri metoder lagt til dem (ikke engang get / set-metoder).

Jeg tror dette viser intensjonen i den type - en struct er bare data som skal drives videre av andre ting.

Svarte 17/03/2009 kl. 19:11
kilden bruker

stemmer
0

De eneste metoder jeg liker å sette på structs tilhører-type metoder, enkle forvandler (og eventuelt for den typen, operatører), og ikke-standard konstruktører.

Eiendommer

For eksempel kan jeg definere en RECTstruct som følger:

typedef struct tagRECT{
    int left;
    int top;
    int right;
    int bottom;

    int get_width(){return right - left;}
    int get_height(){return bottom - top;}
    int set_width(int width){right = left + width; return width;}
    int set_height(int height){bottom = top + height; return height;}
} RECT, *PRECT;

"Set" metoder returnerer den nye verdien for å støtte oppgave kjeding i det tilfellet at kompilatoren støtter egenskaper som en forlengelse.

enkle forvandler

For POD typer som lagrer komplekse data, kan jeg inkludere metoder som utfører enkle transformasjoner på disse dataene. Et opplagt eksempel kan være inkludert rotere, skalere, Shear, og Trans metoder på en TransformationMatrix struct.

operatører

Egentlig bare en forlengelse av det ovennevnte, hvis operatørene fornuftig for den typen, vil jeg legge dem til en struct. Dette kan være hensiktsmessig og nødvendig for å opprettholde den atomicity av objektet. Et opplagt eksempel er standard aritmetiske operatører på en kompleks struct.

Constructors

Jeg foretrekker ikke å ha en standardkonstruktør på mine structs. Jeg ønsker ikke å tildele en rekke PODs og lider påkalling av tusen (eller hva) standard initializations. Hvis memsetinitialisering ikke vil være nok, vil jeg gi en Initial metode.

Jeg vil imidlertid gi en ikke-standardkonstruktør på structs. Dette er spesielt nyttig når ett eller flere felt kan utledes fra delkonstruksjon.

Svarte 17/03/2009 kl. 18:46
kilden bruker

stemmer
0

Hvis det er accesser metoder for data medlemmer så er det en klasse.

Hvis du har direkte tilgang til data og kan endre det på vilje, er det en struct.

Det er ingen grunn til en struct bør ikke ha konstruktører, sammenligningsoperatorer, etc.

Svarte 17/03/2009 kl. 18:38
kilden bruker

stemmer
0

En annen vanlig bruk av structs er for mal ting og lokale RAII eller funktor klasser. Dette er engangs biter av kode som er nærmere funksjoner enn de er til hele klasser, og krever ekstra public:erklæringen er bare dumt. Hvis du ser på boost, vil du se en masse ting som dette:

template<
      bool C
    , typename T1
    , typename T2
    >
struct if_c
{
    typedef T1 type;
};

Det er åpenbart ikke en POD (faktisk den ikke holder noen data i det hele tatt). I andre tilfeller kan du ha RAII klasse som består av bare en konstruktør / desctructor:

struct LogOnLeavingScope : public NonCopyable
{
    string Message;
    LogOnLeavingScope(const string &message) : Message(message) {}
    ~LogOnLeavingScope() { Log(Message); }
];

Funktor klasser vil tilby det samme argument:

struct Logger
{
    void operator()(const string &message) { Log(message); }
}

Jeg vil si det er ingen funksjon i C ++ som innebærer at du bør bruke en klasse i stedet for en struct (bortsett fra kanskje virtuelle funksjoner). Det handler om hva grensesnitt du ønsker å presentere - bruk en struct hvis du er ok med alt som er offentlig. Hvis du synes du har lyst til å legge private:deler, som et godt tegn at du virkelig vil ha en klasse i stedet for en struct.

Svarte 17/03/2009 kl. 15:34
kilden bruker

stemmer
-1

Dette er rent et spørsmål om stil, og hva som er rett eller galt vil bli diktert av din butikk. Noen ganger beslutningen vil være å gjøre noen beslutning, men, for å omskrive Rush, de fortsatt vil ha gjort et valg.

Som en tommelfingerregel vil jeg vanligvis bruker structs for enklere datatyper Rangin fra PODs til objekter som har medlems enkle funksjoner. Men det er ingen lys linje, og når jeg definere en struct Jeg vil normalt ikke gå tilbake og endre den til en klasse bare fordi jeg lagt mer funksjonalitet. Jeg ser ingen verdi i det.

Svarte 17/03/2009 kl. 15:12
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more