C ++ bevat dezelfde vertrouwde sleutelwoorden (bijv. Int) die u herkent van C #. Dit is niet verrassend aangezien beide C-achtige talen zijn. Er is echter één potentiële landmijn die je in de problemen kan brengen. Terwijl C # expliciet de grootten van fundamentele typen definieert (een short is een 16-bits geheel getal, een int is een 32-bits geheel getal, een long is een 64-bits geheel getal, een double is een 64-bits double-precision IEEE 754 zwevend puntnummer, etc.), C ++ biedt dergelijke garanties niet.
De kleinste fundamentele eenheid in C ++ is char, die slechts minimaal groot genoeg moet zijn om de 96 basistekens te bevatten die de C ++ -standaard aangeeft, plus andere tekens in de basistekenset van de implementatie. In theorie zou een implementatie van C ++ een teken kunnen definiëren als 7 bits of 16 bits ... bijna alles is mogelijk. Maar in de praktijk hoeft u zich niet al te veel zorgen te maken over een teken dat iets anders is dan 8 bits (het equivalent van het byte- of sbyte-type in C #), wat de grootte is in Visual C++.
In C ++ zijn char, signed char en unsigned char drie verschillende typen. Alle drie moeten dezelfde hoeveelheid opslag in het geheugen in beslag nemen. Dus een teken in de praktijk is ondertekend of niet ondertekend. Of het is ondertekend of niet ondertekend is de implementatie gedefinieerd (zie de zijbalk). In Visual C ++ is het char-type standaard ondertekend. Maar u kunt een compilerschakelaar gebruiken om deze in plaats daarvan als niet-ondertekend te laten behandelen. In GCC hangt het af van de CPU-architectuur die u target, ongeacht of deze is ondertekend of niet ondertekend.
De getekende integertypes, in grootteorde van kleinste naar grootste, zijn:
De enige garantie voor de grootte van elk van deze typen integer is dat elke ten minste even groot is als het volgende kleinste gehele getal. In Visual C ++ zijn een int en een long int beide 32-bits gehele getallen. Het is alleen de lange lange int die een 64-bits geheel getal is.
Notitie: Je kunt gewoon lang of lang schrijven; je hoeft geen lange int of lange lange int te schrijven. Hetzelfde geldt ook voor een korte int (dat wil zeggen, je kunt gewoon kort schrijven). Het korte type is een 16-bits geheel ondertekend teken in Visual C++.
Elk van de typen integer heeft een overeenkomstig niet-ondertekend geheel-getaltype. U plaatst het sleutelwoord simpelweg niet vooraan om de niet-ondertekende versie te krijgen (behalve voor ondertekende tekens, die u wijzigt in niet-ondertekende tekens).
Als u ervoor wilt zorgen dat u specifieke formaten gebruikt, kunt u het headerbestand C ++ Standard Library opnemen cstdint
(bijv., #include
), die onder andere de typen definieert:
Deze typen kunnen worden gebruikt, maar u zult merken dat de meeste API's deze niet gebruiken; in plaats daarvan gebruiken ze de basistypen rechtstreeks. Dit kan je programmering verwarrend maken, omdat je constant het onderliggende fundamentele type moet controleren om ervoor te zorgen dat je niet eindigt met onbedoelde afknotting of uitbreiding.
Deze typen kunnen meer in gebruik komen, dus ik raad aan om van tijd tot tijd te controleren op hun gebruik in belangrijke bibliotheken en API's en uw code dienovereenkomstig aan te passen als ze op grote schaal worden gebruikt. Natuurlijk, als je absoluut een variabele nodig hebt om bijvoorbeeld een 32-bits geheel getal zonder teken te zijn, moet je zeker gebruik maken van uint32_t en aanpassingen doen voor API-aanroepen en draagbaarheid als dat nodig is.
Drijvende-kommagetallen zijn hetzelfde voor regels voor grootvolgordes. Ze gaan van float naar double naar long double. In Visual C ++ is float een 32-bits drijvende-kommawaarde en dubbele en lange dubbele zijn beide 64-bits drijvende-kommagetallen (lange dubbele is niet groter dan dubbele, met andere woorden).
C ++ heeft geen native type dat vergelijkbaar is met het decimale type van C #. Echter, een van de leuke dingen over C ++ is dat er meestal een groot aantal gratis of goedkope bibliotheken zijn die je kunt licenseren. Er zijn bijvoorbeeld de decNumber-bibliotheek, de Intel Decimal Floating Point Math Library en de GNU Multiple Precision Arithmetic Library. Niets is exact compatibel met het decimale type van C #, maar als u alleen voor Windows-systemen schrijft, kunt u het gegevenstype DECIMAL gebruiken om die compatibiliteit te krijgen, indien nodig, samen met de rekenkundige teken voor decimalen en de conversiefuncties voor gegevenstypen..
Er is ook een Booleaanse type, bool, dat waar of onwaar kan zijn. In Visual C ++ neemt een bool een byte op. In tegenstelling tot C # kan een bool worden omgezet in een integer-type. Wanneer false, heeft het een integer-equivalentwaarde van 0, en als het true is, heeft het een waarde van 1. Dus de statement bool result = true == 1; compileert en resultaat zal evalueren naar waar wanneer de instructie is uitgevoerd.
Dan is er het type Wchar_t, dat een breed karakter heeft. De grootte van een breed personage varieert op basis van het platform. Op Windows-platforms is dit een 16-bits teken. Het is het equivalent van het char type van C #. Het wordt vaak gebruikt om tekenreeksen te construeren. We zullen strings in een ander hoofdstuk bespreken, omdat veel varianten voor strings kunnen worden gebruikt.
Ten slotte is er het ongeldige type, dat op dezelfde manier wordt gebruikt als in C #. En er is een std :: nullptr_t type, wat slordig is om het goed uit te leggen, maar eigenlijk is er het type van de nullptr letterlijk, dat is wat je zou moeten gebruiken in plaats van NULL of een letterlijke 0 (nul) om te controleren op nul waarden.
Opsommingen zijn redelijk vergelijkbaar met elkaar in C ++ en C #. C ++ heeft twee soorten enums: scoped en un-scoped.
Een scoped-opsomming wordt gedefinieerd als een enum-klasse of een enum-struct. Er is geen verschil tussen de twee. Een niet-scoped opsomming wordt gedefinieerd als een eenvoudige opsomming. Laten we naar een voorbeeld kijken:
Voorbeeld: EnumSample \ EnumSample.cpp
#include#include #include # include "... /pchar.h" enum class Kleur Rood, Oranje, Geel, Blauw, Indigo, Violet; // U kunt elk gewenst integraal type opgeven, op voorwaarde dat het past. enum Smaak: niet-ondertekende korte int Vanilla, Chocolate, Strawberry, Mint,; int _pmain (int / * argc * /, _pchar * / * argv * / []) Flavor f = Vanilla; f = Mint; // Dit is legaal, omdat de Flavour-enum een niet-geijkte opsomming is. Kleur c = Kleur :: Oranje; // c = oranje; // Dit is illegaal omdat de Color-enum een scoped enum is. std :: wstring smaak; std: wstring kleur; schakelen (c) geval Kleur :: Rood: kleur = L "Rood"; breken; Kleur: Oranje: kleur = L "Oranje"; breken; kastje Kleur: Geel: kleur = L "Geel"; breken; Kleur: Blauw: kleur = L "Blauw"; breken; Kleur: Indigo: color = L "Indigo"; breken; Kleur: Violet: color = L "Violet"; breken; standaard: color = L "Unknown"; breken; schakelaar (f) kastje Vanille: smaak = L "Vanille"; breken; case Chocolade: flavor = L "Chocolate"; breken; case Strawberry: flavor = L "Strawberry"; breken; geval Mint: flavor = L "Mint"; breken; standaard: pauze; 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;
Deze code geeft de volgende uitvoer:
Smaak is Mint (3). Kleur is oranje (1). De grootte van Flavor is 2. De grootte van Kleur is 4.
Zoals u kunt zien in het voorbeeld, moet u voor de opgesomde kleurnumeratie toegang krijgen tot de leden op dezelfde manier als C # door het opsommingslid vooraf te laten gaan met de naam van de opsomming en de operator voor de bereikresolutie. Daarentegen kunt u met de niet-afgebakende opsomming van aroma's eenvoudig de leden opgeven zonder een voorvoegsel. Om deze reden denk ik dat het beter is om de voorkeur te geven aan scoped opsommingen: je minimaliseert de risico's van het noemen van botsingen en het verminderen van naamruimte vervuiling.
Merk op dat er nog een verschil is met scoped opsommingen: Toen we de numerieke waarde van de scoped Color enum wilden uitvoeren, moesten we de operator static_cast gebruiken om het naar een int te converteren, terwijl we geen casting voor de VN hoefden te doen -scoped Sumerietelling.
Voor de opsomming van aroma's hebben we het onderliggende type opgegeven als een korte int. U kunt ook het onderliggende type voor scoped opsommingen opgeven. Het opgeven van het onderliggende type is optioneel, maar is verplicht als u een forward-declaratie wilt gebruiken met een niet-scoped opsomming. Doorstuurverklaring is een manier om de compileertijden van programma's te versnellen door de compiler alleen te vertellen wat hij moet weten over een type in plaats van het te dwingen om het volledige headerbestand te compileren, zoals het type is gedefinieerd in.
We zullen dit later bekijken. Voor nu, onthoud alleen dat voor een onbereikbare opsomming het onderliggende type expliciet moet worden gespecificeerd om een voorwaartse verklaring ervan te gebruiken; een scope-opsomming vereist geen specificatie van het onderliggende type om een forward-declaratie ervan te gebruiken (het onderliggende type zal int zijn als er geen is opgegeven).
Je kunt hetzelfde doen met opsommingen in C ++ als je kunt in C # in termen van het expliciet toewijzen van waarden aan leden, en in termen van het maken van markeringen opsommingen. Je doet het allemaal op dezelfde manier, behalve dat je niets als het FlagAttribute in C ++ hoeft toe te passen om flag-opsommingen te maken; u wijst gewoon de juiste waarden toe en gaat daar verder.
std :: wcout
, std :: wcerr
, std :: wcin
De standaard: 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.
Om je te laten zien hoe de invoer er uitziet, hier is een voorbeeld.
Voorbeeld: ConsoleSample \ ConsoleSample.cpp
#include#include #include # include "... /pchar.h" struct Color float ARGB [4]; ongeldig A (zwevende waarde) ARGB [0] = waarde; float A (void) const return ARGB [0]; void R (float value) ARGB [1] = waarde; float R (void) const return ARGB [1]; void G (zwevende waarde) ARGB [2] = waarde; float G (void) const return ARGB [2]; void B (zwevende waarde) ARGB [3] = waarde; float B (void) const return ARGB [3]; ; // Dit is een zelfstandige functie, die toevallig een binaire // -operator is voor de << 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 >> zelfstandig naamwoord; 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;
De vorige code is een vrij eenvoudige demo. Er is bijvoorbeeld geen foutcontrole. Als u dus een onjuiste waarde invoert voor het gehele getal, loopt deze gewoon door tot het einde, waarbij std :: wcin onmiddellijk wordt geretourneerd zonder gegevens (dat is wat het doet tenzij en totdat u de fout oplost).
Als u geïnteresseerd bent in iostream-programmering, inclusief het gebruik van dingen als std :: wofstream om gegevens naar een bestand uit te voeren en std :: wifstream om gegevens uit een bestand te lezen (ze werken op dezelfde manier als std: wcout en std: wcin, alleen met extra functionaliteit voor het omgaan met het feit dat ze met bestanden werken), zie de MSDN iostream-programmeerpagina's. Door alle ins en outs van streams te leren, kan een boek eenvoudig alleen worden gevuld.
Nog een laatste ding. Je hebt ongetwijfeld opgemerkt dat de streamfunctionaliteit een beetje vreemd lijkt met de bitverschuivingsoperators << and >>. Dat komt omdat deze operators zijn overbelast. Hoewel u zou verwachten dat de bit-shiftoperators op een bepaalde manier op gehele getallen werken, is er geen specifieke verwachting die u waarschijnlijk heeft over hoe ze moeten werken wanneer ze respectievelijk worden toegepast op een uitvoerstroom of een invoerstroom. Dus de C ++ Standard Library-streams hebben deze operators gecoöpteerd om ze te gebruiken voor het invoeren en uitvoeren van gegevens naar streams. Wanneer we de mogelijkheid willen hebben om een aangepast type dat we hebben gemaakt (zoals de vorige kleurstructuur) in te lezen of weg te schrijven, moeten we eenvoudig een geschikte operatoroverbelasting creëren. We zullen later in het boek meer leren over overbelasting van de operator, dus maak je geen zorgen als het op dit moment een beetje verwarrend is
Het verschil tussen een klasse en een structuur in C ++ is simpelweg dat de leden van een structuur standaard zijn voor publiek, terwijl de leden van een klasse standaard zijn ingesteld op privé. Dat is het. Ze zijn overigens hetzelfde. Er is geen onderscheid tussen het waardetype en het verwijzingstype zoals er in C # is.
Dat gezegd hebbende, ziet u meestal dat programmeurs klassen gebruiken voor uitgebreide typen (combinaties van gegevens en functies) en structuren voor eenvoudige gegevenstypen. Normaal gesproken is dit een stilistische keuze die de niet-objectgerichte oorsprong van de structuur in C vertegenwoordigt, waardoor het gemakkelijk is om snel te differentiëren tussen een eenvoudige gegevenscontainer versus een volledig object door te kijken of het een structuur of een klasse is. Ik raad aan deze stijl te volgen.
Notitie: Een uitzondering op deze stijl is wanneer een programmeur code schrijft die bedoeld is voor gebruik in zowel C als C ++. Omdat C geen klasse-type heeft, kan het structuurtype in plaats daarvan op dezelfde manier worden gebruikt als hoe u een klasse in C ++ zou gebruiken. Ik ga niet schrijven over het schrijven van C-compatibele C ++ in dit boek. Om dit te doen, zou u bekend moeten zijn met de C-taal en de verschillen tussen deze taal en C ++. In plaats daarvan concentreren we ons op het schrijven van schone, moderne C ++-code.
In Windows Runtime ("WinRT") -programmering kan een openbare structuur alleen gegevensleden hebben (geen eigenschappen of functies). Deze gegevensleden kunnen alleen worden samengesteld uit fundamentele gegevenstypen en andere openbare structuren - die uiteraard dezelfde beperkingen hebben die alleen betrekking hebben op gegevens, fundamentele en openbare structuren. Houd hier rekening mee als u met C werkt in Metro-achtige apps voor Windows 8++.
Soms zie je het friend-sleutelwoord gebruikt in een klassendefinitie. Het wordt gevolgd door een klassenaam of een functieverklaring. Wat dit code-construct doet, is die klas- of functietoegang tot de niet-publieke lidgegevens en -functies van de klasse. Meestal wilt u dit voorkomen, omdat uw klas normaal gesproken alles wat u wilt blootstellen via de openbare interface van uw klas moet laten zien. Maar in die zeldzame gevallen waarin u bepaalde gegevensleden of lidfuncties niet publiekelijk wilt tonen, maar wel wilt dat een of meer klassen of functies er toegang toe hebben, kunt u het friend-sleutelwoord gebruiken om dit te bereiken.
Aangezien klassen een zeer belangrijk onderdeel zijn van C ++ -programmering, zullen we ze verderop in het boek in meer detail bekijken.
Het type vakbond is een beetje vreemd, maar het heeft zijn toepassingen. Je zult het van tijd tot tijd tegenkomen. Een unie is een gegevensstructuur die veel gegevensleden lijkt te bevatten, maar u alleen toestaat om een van de gegevensleden ervan tegelijkertijd te gebruiken. Het eindresultaat is een datastructuur die u vele mogelijke toepassingen biedt zonder geheugenverlies. De grootte van de vakbond moet groot genoeg zijn om alleen het grootste lid van de vakbond te bevatten. In de praktijk betekent dit dat de gegevensleden elkaar in het geheugen overlappen (u kunt dus slechts één tegelijk gebruiken). Dit betekent ook dat je niet weet wat het actieve lid van een vakbond is, tenzij je het op de een of andere manier bijhoudt. Er zijn veel manieren om dat te doen, maar een unie en een opsomming in een structuur plaatsen is een goede, eenvoudige, opgeruimde manier om het te doen. Hier is een voorbeeld.
Voorbeeld: UnionSample \ UnionSample.cpp
#include#include # include "... /pchar.h" enum class SomeValueDataType Int = 0, Float = 1, Double = 2; struct SomeData SomeValueDataType Type; union int iData; zweven fData; dubbele dData; Waarde; SomeData (void) SomeData (0); SomeData (int i) Type = SomeValueDataType :: Int; Value.iData = i; SomeData (float f) Type = SomeValueDataType :: Float; Value.fData = f; SomeData (dubbele 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;
Zoals je ziet, definiëren we een opsomming met leden die elk van de soorten leden van de unie vertegenwoordigen. Vervolgens definiëren we een structuur die zowel een variabele van het type van die enum bevat als een anonieme unie. Dit geeft ons alle informatie die we nodig hebben om te bepalen welk type de unie momenteel binnen één ingekapseld pakket heeft.
Als u wilt dat de unie in meerdere structuren kan worden gebruikt, kunt u deze buiten de structuur verklaren en deze een naam geven (bijv. Unie SomeValue ...;). U zou het dan binnen de structuur kunnen gebruiken als, bijvoorbeeld, Waarderingswaarde ;. Het is meestal beter om het te houden als een anonieme unie, omdat je je geen zorgen hoeft te maken over de bijwerkingen van het maken van een verandering, behalve binnen de structuren waarin het is gedefinieerd.
Vakbonden kunnen constructeurs, destructors en lidfuncties hebben. Maar aangezien ze maar één actief lid hebben, heeft het zelden zin om lidfuncties voor een vakbond te schrijven. Je zult ze zelden zien, misschien nooit.
Het eerste dat je moet begrijpen over typedef is dat typedef, ondanks de implicaties van de naam, geen nieuwe typen maakt. Het is een aliasing-mechanisme dat voor veel dingen kan worden gebruikt.
Het wordt veel gebruikt bij de implementatie van de C ++ Standard Library en andere op sjablonen gebaseerde code. Dit is, misschien wel het belangrijkste gebruik ervan. We zullen het verder verkennen in het hoofdstuk over sjablonen.
Het kan je een hoop typewerk besparen (hoewel dit argument een deel van zijn kracht verloor met de herbestemming van het automatische sleutelwoord voor type deductie in C ++ 11). Als u een bijzonder gecompliceerd gegevenstype hebt, maakt u hiervoor een typedef, dus u hoeft het maar een keer te typen. Als het doel van uw gecompliceerde gegevenstype onduidelijk is, kan het een begrijpelijker naam geven met een typedef, waardoor uw programma gemakkelijker te begrijpen is.
Het wordt soms gebruikt als een abstractie door ontwikkelaars om eenvoudig een achtergrondtype te veranderen (bijv. Van een std :: vector naar een std :: lijst) of het type van een parameter (bijv. Van een int in een lange). Voor uw eigen code voor intern gebruik moet dit worden afgekeurd. Als u code ontwikkelt die anderen zullen gebruiken, zoals een bibliotheek, zou u nooit moeten proberen om een typedef op deze manier te gebruiken. Het enige wat u doet, is het verminderen van de vindbaarheid van het doorbreken van wijzigingen in uw API als u een typedef wijzigt. Gebruik ze om een semantische context toe te voegen, zeker, maar gebruik ze niet om een onderliggend type te wijzigen in code waar anderen op vertrouwen.
Als u het type iets moet wijzigen, onthoud dan dat elke wijziging in de parameters van een functie een brekingswijziging is, net zoals een wijziging in retourtype of de toevoeging van een standaardargument. De juiste manier om met de mogelijkheid van een toekomstige typeverandering om te gaan, is met abstracte klassen of met sjablonen (afhankelijk van wat het meest geschikt is of wat u maar wilt, als beide geschikt zijn). Op deze manier zal de openbare interface naar uw code niet veranderen, alleen de implementatie zal. Het idioom van Pimpl is een andere goede manier om een stabiele API te behouden met behoud van de vrijheid om de implementatiedetails te veranderen. We zullen in een later hoofdstuk het idioom van Pimpl verkennen, een afkorting voor 'pointer to implementation'.
Hier is een klein codeblok dat de syntaxis voor typedef illustreert.
klasse ExistingType; typedef ExistingType AliasForExistingType;
En het volgende is een kort voorbeeld dat laat zien hoe typedef kan worden gebruikt. Het doel van dit voorbeeld is om een vereenvoudigd maar realistisch gebruik van een typedef te illustreren. In de praktijk zou een typedef zoals dit in een naamruimte terechtkomen en dan worden opgenomen in een headerbestand. Aangezien we daar geen van hebben behandeld, is dit voorbeeld opzettelijk eenvoudig gehouden.
Voorbeeld: TypedefSample \ TypedefSample.cpp
#include#include #include #include # include "... /pchar.h" // Dit maakt WidgetIdVector een alias voor std :: vector , die // meer betekenis heeft dan std: vector zou hebben, want nu weten we dat // alles met dit alias een vector van widget-ID's // verwacht in plaats van een vector van gehele getallen. typedef std :: vector WidgetIdVector; bool ContainsWidgetId (WidgetIdVector idVector, int id) return (std :: end (idVector)! = std :: find (std :: begin (idVector), std :: end (idVector), id)); int _pmain (int / * argc * /, _pchar * / * argv * / []) WidgetIdVector idVector; // Voeg een aantal ID-nummers toe aan de vector. idVector.push_back (5); idVector.push_back (8); // Geef een resultaat door ons te laten weten of de ID zich in // WidgetIdVector bevindt. std :: wcout << L"Contains 8: " << (ContainsWidgetId(idVector, 8) ? L"true." : L"false.") << std::endl; return 0;
U zou nu een goed begrip moeten hebben van de typen die beschikbaar zijn in C ++. In het volgende artikel bekijken we namespaces in C++.
Deze les staat voor een hoofdstuk uit C ++ Kort gezegd, een gratis eBoek van het team van Syncfusion.