C ++ inneholder de samme kjente søkeordene (for eksempel int) som du gjenkjenner fra C #. Dette er overraskende gitt at begge er C-lignende språk. Det er imidlertid et potensielt landminne som kan kaste deg i vanskeligheter. Mens C # spesifikt definerer størrelsen på grunnleggende typer (en kort er et 16-biters heltall, er et int et 32-biters heltall, en lang er et 64-biters heltall, en dobbel er en 64-bits dobbeltsikkerhets IEEE 754 flytende punktnummer, etc.), C ++ gir ingen slike garantier.
Den minste grunnleggende enheten i C ++ er char, som bare behøver å være minst stor nok til å holde de 96 grunnleggende tegnene som C ++-standarden angir, samt eventuelle andre tegn i implementeringsens grunnleggende tegnsett. I teorien kan noen implementering av C ++ definere en char som 7 bits eller 16 bits ... nesten alt er mulig. Men i praksis trenger du ikke å bekymre deg for mye om en karbon er noe annet enn 8 biter (tilsvarende byte- eller sbyte-typen i C #), som er dens størrelse i Visual C++.
I C + +, karbon, signert kar, og usignert karbon er tre forskjellige typer. Alle tre er pålagt å ta opp samme lagringsplass i minnet. Så et kull i praksis er enten signert eller usignert. Enten det er signert eller usignert, er implementeringen definert (se sidefeltet). I Visual C ++ er char type, som standard, signert. Men du kan bruke en kompilatorswitch for å få den behandlet som usignert i stedet. I GCC, om det er signert eller usignert, avhenger av hvilken CPU-arkitektur du målretter mot.
De signerte heltalltyper, i størrelsesorden fra minste til største, er:
Den eneste garantien for størrelsen på hver av disse heltalltyper er at hver enkelt er minst like stor som den neste minste heltall typen. I Visual C ++ er en int og en lang int begge 32-biters heltall. Det er bare den lange lange inten som er et 64-biters heltall.
Merk: Du kan bare skrive lenge eller lenge lenge; du trenger ikke å skrive lang int eller lang lang int, henholdsvis. Det samme gjelder også for kort int (dvs. du kan bare skrive kort). Den korte typen er et 16-bits signert heltall i Visual C++.
Hver av heltalltypene har en tilsvarende usignert heltalltype. Du legger bare søkeordet usignert foran for å få den usignerte versjonen (unntatt for signert karbon, som du endrer til usignert karbon).
Hvis du trenger å sikre at du bruker bestemte størrelser, kan du inkludere C ++ Standard Library-headerfilen cstdint
(f.eks., #inkludere
), som blant annet definerer typer:
Disse typene har deres bruk, men du vil oppdage at de fleste APIer ikke bruker dem; I stedet bruker de de grunnleggende typene direkte. Dette kan gjøre programmeringen forvirrende, siden du hele tiden må sjekke den underliggende grunnleggende typen for å sikre at du ikke ender med utilsiktet avkorting eller utvidelse.
Disse typene kan komme i bruk mer, så jeg anbefaler at du kontrollerer bruken av dem i større biblioteker og APIer fra tid til annen og justerer koden din hvis de blir allment vedtatt. Selvfølgelig, hvis du absolutt trenger en variabel for å være for eksempel et usignert 32-biters heltall, bør du helt sikkert gjøre bruk av uint32_t og gjøre noen tilpasninger for API-samtaler og overførbarhet etter behov.
Flytepunktstallene er de samme som størrelsesordreregler. De går fra flyte til dobbelt og lang dobbel. I Visual C ++ er float et 32-biters flytende punktnummer og dobbelt og lang dobbel er begge 64-biters flytende punktnumre (lang dobbel er ikke større enn dobbel, med andre ord).
C ++ har ingen innfødt type som kan sammenlignes med C # s desimaltype. Men en av de fine tingene om C ++ er at det vanligvis finnes et stort antall gratis eller rimelige biblioteker som du kan lisensiere. For eksempel er det DecNumber-biblioteket, Intel Decimal Floating Point Math Library og GNU Multiple Precision Arithmetic Library. Ingen er nøyaktig kompatibel med C # s desimaltype, men hvis du bare skriver for Windows-systemer, kan du bruke DECIMAL datatypen for å få den kompatibiliteten om nødvendig, sammen med de decimale aritmetiske funksjoner og datatype konverteringsfunksjonene.
Det er også en boolsk type, bool, som kan være sann eller falsk. I Visual C ++ tar en bool opp en byte. I motsetning til C # kan en bool omdannes til en heltalltype. Når det er falskt, har det en heltallsverdien på 0, og når den er sann, har den en verdi på 1. Så setningen bool result = true == 1; vil kompilere og resultatet vil evaluere til ekte når setningen er utført.
Deretter er det wchar_t-typen, som har en bred karakter. En bred karakter størrelse varierer basert på plattformen. På Windows-plattformer er det en 16-biters tegn. Det er ekvivalent med C # s karbon type. Det brukes ofte til å konstruere strenge. Vi vil diskutere strenge i et annet kapittel siden mange varianter kan brukes til strenger.
Til slutt er det tomrommet, som brukes på samme måte som i C #. Og det er en std :: nullptr_t-type, som er rotete å forklare riktig, men i utgangspunktet er det å være typen nullptr-bokstavlig, som er hva du bør bruke i stedet for NULL eller en bokstavlig 0 (null) for å sjekke null verdier.
Opptellingen er ganske lik hverandre i C ++ og C #. C ++ har to typer enums: scoped og un-scoped.
En scoped opptelling er definert som enten en enum klasse eller en enum struct. Det er ingen forskjell mellom de to. En un-scoped opptelling er definert som en vanlig enum. La oss se på et utvalg:
Eksempel: EnumSample \ EnumSample.cpp
#inkludere#inkludere #inkludere #include "... /pchar.h" enum class Farge Rød, Orange, Gul, Blå, Indigo, Violett; // Du kan angi hvilken underliggende integral type du vil ha, forutsatt at den passer. enum Smaken: usignert kort int Vanilje, Sjokolade, Jordbær, Mynte,; int _pmain (int / * argc * /, _pchar * / * argv * / []) Flavor f = Vanilje; f = Mint; // Dette er lovlig siden Flavour Enum er en un-scoped enum. Farge c = Farge :: Oransje; // c = Orange; // Dette er ulovlig siden Color enum er en scoped enum. std :: wstring smak; std :: wstring farge; bryter (c) tilfelle Farge :: Rød: Farge = L "Rød"; gå i stykker; case farge :: oransje: color = l "orange"; gå i stykker; case farge :: gul: color = l "gul"; gå i stykker; tilfelle Farge :: Blå: Farge = L "Blå"; gå i stykker; case farge :: indigo: color = l "indigo"; gå i stykker; tilfelle Farge :: Violett: Farge = L "Violett"; gå i stykker; standard: color = L "Unknown"; gå i stykker; bryter (f) tilfelle Vanilje: smak = L "Vanilje"; gå i stykker; Veske Sjokolade: Smaken = L "Sjokolade"; gå i stykker; tilfelle Jordbær: smak = L "Jordbær"; gå i stykker; case Mint: smaken = L "Mint"; gå i stykker; standard: break; std :: wcout << L"Flavor is " << flavor.c_str() << L" (" << f << L"). Color is " << color.c_str() << L" (" << static_cast (C) << L")." << std::endl << L"The size of Flavor is " << sizeof(Flavor) << L"." << std::endl << L"The size of Color is " << sizeof(Color) << L"." << std::endl; return 0;
Denne koden vil gi følgende utgang:
Smaken er Mint (3). Farge er oransje (1). Størrelsen på Flavour er 2. Størrelsen på Farge er 4.
Som du kan se i prøven, krever den scoped Color-talloppstillingen at du skal få tilgang til sine medlemmer på samme måte som C # ved å forhåndsføre nummereringsmedlemmet med opptellingenes navn og omfangsoppløsningsoperatøren. I motsetning till den un-scoped Flavour-oppregningen kan du ganske enkelt spesifisere medlemmene uten prefiks. Av denne grunn tror jeg det er bedre å foretrekke å foretrekke scoped opptegnelser: Du minimerer risikoen for å navngi kollisjoner og redusere navngitte forurensninger.
Legg merke til at det er en annen forskjell med scoped-tall: Når vi ønsket å utføre numerisk verdi for Scoped Color Enum, måtte vi bruke static_cast-operatøren til å konvertere den til en int, mens vi ikke behøvde å gjøre noen støping for un -skrevet smaksopptelling.
For smaksopptellingen spesifiserte vi den underliggende typen som en usignert kort int. Du kan også spesifisere den underliggende typen for scoped opptellingene. Angi den underliggende typen er valgfri, men er obligatorisk hvis du ønsker å bruke fremoverdeklarasjon med en ikke-scoped opptelling. Videresendelsesangivelse er en måte å øke programkompileringstiden ved kun å fortelle kompilatoren hva den trenger å vite om en type, snarere enn å tvinge den til å kompilere hele headerfilen typen er definert i.
Vi vil se på dette senere. For nå, husk bare at en ikke-scoped opptelling må ha sin underliggende type eksplisitt spesifisert for å kunne bruke en forward-erklæring om den; en rekkevidde oppsummering krever ikke spesifikasjon av sin underliggende type for å bruke en forward-erklæring av den (den underliggende typen vil være int hvis ingen er spesifisert).
Du kan gjøre det samme med summeringer i C ++ som du kan i C # når det gjelder eksplisitt tilordne verdier til medlemmer, og når det gjelder å opprette flaggopptegnelser. Du gjør det på samme måte, med unntak av at du ikke trenger å bruke noe som FlagAttribute i C ++ for å lage flaggetaleringer; Du tilordner bare de riktige verdiene og fortsetter derfra.
std :: wcout
, std :: wcerr
, std :: wcin
Std :: wcout << L”Flavor… code outputs wide character data to the standard output stream. In the case of a console program such as this, the standard output is the console window. There is also a std::wcerr output stream, which will output wide character data to the standard error output stream. This is also the console window, but you can redirect std::wcout output to one file and std::wcerr output to another file. There is also a std::wcin for inputting data from the console. We won't explore this, nor will we explore their byte counterparts: std::cout, std::cerr, and std::cin.
Bare for å la deg se hvordan innspillet ser ut, her er et eksempel.
Eksempel: ConsoleSample \ ConsoleSample.cpp
#inkludere#inkludere #inkludere #include "... /pchar.h" struct Farge float ARGB [4]; tomrom A (flytverdien) ARGB [0] = verdi; flyte A (void) const return ARGB [0]; tomrom R (flytverdien) ARGB [1] = verdi; flyte R (void) const return ARGB [1]; tomrom G (flytverdien) ARGB [2] = verdi; flyte G (void) const return ARGB [2]; tomrom B (flytverdien) ARGB [3] = verdi; float B (void) const return ARGB [3]; ; // Dette er en frittstående funksjon, som skjer med en binær // operatør for << operator when used with a wostream on // the left and a Color instance on the right. std::wostream& operator<<(std::wostream& stream, const Color& c) stream << L"ARGB: " << c.A() << L"f, " << c.R() << L"f, " << c.G() << L"f, " << c.B() << L"f "; return stream; int _pmain(int /*argc*/, _pchar* /*argv*/[]) std::wcout << L"Please input an integer and then press Enter: "; int a; std::wcin >> a; std :: wcout << L"You entered '" << a << L"'." << std::endl; std::wcout << std::endl << L"Please enter a noun (one word, no spaces) " << L"and then press Enter: "; std::wstring noun; // wcin breaks up input using white space, so if you include a space or // a tab, then it would just put the first word into noun and there // would still be a second word waiting in the input buffer. std::wcin >> substantiv std :: wcerr << L"The " << noun << L" is on fire! Oh no!" << std::endl; Color c = 100.0f/255.0f, 149.0f/255.0f, 237.0f/255.0f, 1.0f ; // This uses our custom operator from above. Come back to this sample // later when we've covered operator overloading and this should make // much more sense. std::wcout << std::endl << L"Cornflower Blue is " << c << L"." << std::endl; return 0;
Den forrige koden er en ganske enkel demo. Den har ingen feilkontroll, for eksempel. Så, hvis du oppgir en feil verdi for heltallet, vil den bare gå gjennom til slutten med std :: wcin returnere umiddelbart uten noen data (det er det det gjør med mindre og til du løser feilen).
Hvis du er interessert i iostream programmering, inkludert bruk av ting som std :: wofstream å utdata data til en fil og std :: wifstream for å lese data inn fra en fil (de fungerer det samme som std :: wcout og std :: wcin, bare med ekstra funksjonalitet for å håndtere det faktum at de jobber med filer), se MSDN iostream programmeringssider. Lære alle innsatsene i bekker kan enkelt fylle en bok bare på egenhånd.
En siste ting skjønt. Du har utvilsomt lagt merke til at strømfunksjonaliteten ser litt rar ut med bitskiftende operatører << and >>. Det er fordi disse operatørene har blitt overbelastet. Mens du forventer at bitskiftoperatørene skal virke på en bestemt måte på heltall, er det ikke noen bestemt forventning du sannsynligvis vil ha om hvordan de skal fungere når de brukes på henholdsvis en utgangsstrøm eller en inngangsstrøm. Så C ++ Standard Library-strømmene har samordnet disse operatørene for å bruke dem til å skrive inn og utdata til strømmer. Når vi vil ha muligheten til å lese inn eller skrive ut en egendefinert type som vi har opprettet (for eksempel den forrige fargestrukturen), trenger vi bare å opprette en passende operatøroverbelastning. Vi lærer mer om overbelastning av operatør senere i boken, så vær ikke bekymret hvis det er litt forvirrende akkurat nå
Forskjellen mellom en klasse og en struktur i C ++ er rett og slett at en strukturens medlemmer er standard for offentlige, mens en klasses medlemmer er standard for private. Det er det. De er ellers de samme. Det er ingen verdi-type versus referanse-type skillnad som det er i C #.
Når det er sagt, vil du vanligvis se programmerere bruke klasser for detaljerte typer (kombinasjoner av data og funksjoner) og strukturer for enkle data-bare typer. Normalt er dette et stilistisk valg som representerer ikke-objektorienterte strukturer i C, noe som gjør det enkelt å skille seg raskt mellom en enkel datakontainer og en fullblåst gjenstand ved å se om den er en struktur eller en klasse. Jeg anbefaler å følge denne stilen.
Merk: Et unntak fra denne stilen er hvor en programmerer skriver kode som skal brukes både i C og C ++. Siden C ikke har en klassetype, kan strukturtypen i stedet brukes på måter som ligner på hvordan du vil bruke en klasse i C ++. Jeg kommer ikke til å dekke å skrive C-kompatibel C ++ i denne boken. For å gjøre det, må du være kjent med C-språket og forskjellene mellom det og C ++. I stedet fokuserer vi på å skrive ren, moderne C ++-kode.
I Windows Runtime ("WinRT") programmering, kan en offentlig struktur kun ha data medlemmer (ingen egenskaper eller funksjoner). Disse datamedlemmene kan bare bestå av grunnleggende datatyper og andre offentlige strukturer - som selvsagt har de samme begrensningene for data-bare, grunnleggende og offentlig-strukturer. Husk dette hvis du jobber med noen Metro-stil apps for Windows 8 ved hjelp av C++.
Du vil noen ganger se vennens søkeord som brukes i en klasses Definisjon. Det følges av enten et klassenavn eller en funksjonsdeklarasjon. Hva denne koden konstruksjon gjør er å gi den klassen eller funksjonen tilgang til de ikke-offentlige medlemsdata og funksjoner i klassen. Vanligvis vil du unngå dette, siden klassen din normalt skal avsløre alt du vil utsette gjennom det offentlige grensesnittet. Men i de sjeldne tilfellene der du ikke ønsker å offentliggjøre visse datamedlemmer eller medlemsfunksjoner offentlig, men vil ha en eller flere klasser eller funksjoner for å få tilgang til det, kan du bruke vennens søkeord for å oppnå dette.
Som klasser er en svært viktig del av C ++ programmering, vil vi utforske dem i mye mer detalj senere i boken.
Fellesskapstypen er litt rar, men den har sine bruksområder. Du vil møte det fra tid til annen. En union er en datastruktur som ser ut til å inneha mange data medlemmer, men bare lar deg bruke en av sine data medlemmer til enhver tid. Slutresultatet er en datastruktur som gir deg mange mulige anvendelser uten å kaste bort minne. Størrelsen på foreningen er nødvendig for å være stor nok til bare å inneholde det største medlemmet av foreningen. I praksis betyr dette datamedlemmene overlapper hverandre i minnet (derfor kan du kun bruke en om gangen). Dette betyr også at du ikke har mulighet til å vite hva det aktive medlemmet av en union er, med mindre du holder øye med det på en eller annen måte. Det er mange måter du kan gjøre, men å sette en union og en enum i en struktur er en god, enkel og ryddig måte å gjøre det på. Her er et eksempel.
Eksempel: UnionSample \ UnionSample.cpp
#inkludere#inkludere #include "... /pchar.h" enum class SomeValueDataType Int = 0, Float = 1, Double = 2; struct SomeData SomeValueDataType Type; union int iData; float fData; dobbel dData; Verdi; SomeData (void) SomeData (0); SomeData (int i) Type = SomeValueDataType :: Int; Value.iData = i; SomeData (float f) Type = SomeValueDataType :: Float; Value.fData = f; SomeData (double d) Type = SomeValueDataType :: Double; Value.dData = d; ; int _pmain (int / * argc * /, _pchar * / * argv * / []) SomeData data = SomeData (2.3F); std :: wcout << L"Size of SomeData::Value is " << sizeof(data.Value) << L" bytes." << std::endl; switch (data.Type) case SomeValueDataType::Int: std::wcout << L"Int data is " << data.Value.iData << L"." << std::endl; break; case SomeValueDataType::Float: std::wcout << L"Float data is " << data.Value.fData << L"F." << std::endl; break; case SomeValueDataType::Double: std::wcout << L"Double data is " << data.Value.dData << L"." << std::endl; break; default: std::wcout << L"Data type is unknown." << std::endl; break; return 0;
Som du kan se, definerer vi en enum som har medlemmer som representerer hver av medlemmene av foreningen. Vi definerer da en struktur som inneholder både en variabel av typen den enum og deretter en anonym union. Dette gir oss all den informasjonen vi trenger for å bestemme hvilken type foreningen for tiden holder inne i en innkapslet pakke.
Hvis du vil at foreningen skal kunne brukes i flere strukturer, kan du deklarere den utenfor strukturen og gi den et navn (f.eks. Union SomeValue ...;). Du kan da bruke den i strukturen som for eksempel SomeValue Value ;. Det er vanligvis bedre å beholde det som en anonym union, men siden du ikke trenger å bekymre deg for bivirkningene ved å gjøre endringer, unntatt innenfor de strukturer som den er definert i..
Fagforeninger kan ha konstruktører, destruktorer og medlemsfunksjoner. Men siden de bare har ett aktivt data medlem, er det sjelden noe som helst å skrive medlemsfunksjoner for en union. Du vil sjelden se dem, kanskje aldri.
Den første tingen å forstå om typedef er at til tross for implikasjonene i navnet, oppretter ikke typedef nye typer. Det er en aliasing mekanisme som kan brukes til mange ting.
Det brukes mye i implementering av C ++ Standard Library og annen malbasert kode. Dette er uten tvil den viktigste bruken. Vi vil utforske det mer i kapitlet om maler.
Det kan spare deg for mye å skrive (selv om dette argumentet mistet noe av sin kraft med omplassering av automatisk søkeordet for typen fradrag i C + + 11). Hvis du har en spesielt komplisert datatype, betyr det å lage en typedef for det bare at du må skrive det ut en gang. Hvis den kompliserte datatypens formål er uklar, gir det et mer semantisk meningsfylt navn med en typedef, kan bidra til å gjøre programmet lettere å forstå.
Det brukes noen ganger som en abstraksjon av utviklere for enkelt å endre en backing type (for eksempel fra en std :: vektor til en std :: liste) eller typen av en parameter (for eksempel fra en int til en lang). For din egen interne brukskode, bør dette bli frynst på. Hvis du utvikler kode som andre vil bruke, for eksempel et bibliotek, bør du aldri prøve å bruke en typedef på denne måten. Alt du gjør, er å redusere oppdagelsen av å bryte endringer i APIen din hvis du endrer en typedef. Bruk dem til å legge til semantisk kontekst, men ikke bruk dem til å endre en underliggende type i kode som andre stoler på.
Hvis du trenger å endre type noe, husk at en endring i en funksjons parametere er en bryteendring, som er en endring i returtype eller tillegg av et standardargument. Den riktige måten å håndtere muligheten for en fremtidig typeendring er med abstrakte klasser eller med maler (avhengig av hva som passer best, eller avhengig av hva du vil, hvis begge skal fungere). På denne måten vil det offentlige grensesnittet til koden din ikke endres, bare implementeringen vil. Pimpl-idiomet er en annen god måte å holde en stabil API samtidig som du beholder friheten til å endre implementeringsdetaljer. Vi vil utforske Pimpl-idiomet, kort for «peker til implementering», i et senere kapittel.
Her er en liten kodeblokk som illustrerer syntaksen for typedef.
klasse EksisterendeType; typedef EksisterendeType AliasForExistingType;
Og følgende er en kort prøve som viser hvordan typedef kan brukes. Formålet med denne prøven er å illustrere en forenklet men realistisk bruk av en typedef. I praksis vil en typedef som dette gå inn i et navneområde og vil da bli inkludert i en headerfil. Siden vi ikke har dekket noe av det, har dette eksemplet blitt holdt med vilje.
Eksempel: TypedefSample \ TypedefSample.cpp
#inkludere#inkludere #inkludere #inkludere #include "... /pchar.h" // Dette gjør WidgetIdVector et alias for std :: vector , som har // mer betydning enn std :: vektor ville ha, siden nå vet vi at / / noe som bruker dette aliaset forventer en vektor av widget-IDer // i stedet for en vektor av heltall. typedef std :: vektor WidgetIdVector; bool ContainsWidgetId (WidgetIdVector idVector, int id) return (std :: end (idVector)! = std :: finn (std :: start (idVector), std :: end (idVector), id)); int _pmain (int / * argc * /, _pchar * / * argv * / []) WidgetIdVector idVector; // Legg til noen id-numre til vektoren. idVector.push_back (5); idVector.push_back (8); // Utfør et resultat som lar oss få vite om id er i // WidgetIdVector. std :: wcout << L"Contains 8: " << (ContainsWidgetId(idVector, 8) ? L"true." : L"false.") << std::endl; return 0;
Du bør nå ha en klar forståelse av hvilke typer som er tilgjengelige i C ++. I neste artikkel tar vi nærmere titt på navneområder i C++.
Denne leksjonen representerer et kapittel fra C ++ Succinctly, en gratis eBok fra teamet på Syncfusion.