Video: Privacy, Security, Society - Computer Science for Business Leaders 2016 2025
Av Stephen R. Davis
C ++ er ikke et lett programmeringsspråk for å mestre. Bare gjennom erfaring vil de myriade kombinasjonene av symboler begynne å virke naturlig for deg. Dette Cheat Sheet gir deg imidlertid gode tips om å lette overgangen fra C ++ nybegynner til C ++ guru: Vet hvordan du leser komplekse C ++-uttrykk; lære å unngå pekerproblemer; og finne ut hvordan og når du skal lage dype kopier.
Slik leser du en kompleks C ++-uttrykk
C ++ er full av små symboler, som hver legger til betydningen av uttrykk. Reglene for C ++ grammatikk er så fleksible at disse symbolene kan kombineres i nesten ufattelig komplekse kombinasjoner. Uttrykk i det enklere C-språket kan bli så stygt at det pleide å være en årlig konkurranse for hvem som kunne skrive det mest uklare programmet, og hvem kunne forstå det.
Det er aldri en god ide å prøve å skrive komplisert kode, men du vil noen ganger kjøre over uttrykk i C ++ som er litt forvirrende ved første øyekast. Bare bruk følgende trinn for å finne ut dem:
-
Start ved de fleste innebygde parenteser.
Begynn å lete etter de ytre mest parentesene. Innenfor disse, se etter innebygde parenteser. Gjenta prosessen til du har jobbet deg til det dypeste par parentes. Begynn å evaluere den subekspresjonen først ved å bruke følgende regler. Når du forstår det uttrykket, hopper du tilbake til neste nivå og gjentar prosessen.
-
Inne i paret parentes, vurder hver operasjon i forrang.
Bestillingen som operatørene evalueres av, bestemmes av operatørens forrang som er vist i tabellen. Indirection kommer før multiplikasjon som kommer før tilsetning dermed legger følgende 1 pluss 2 ganger verdien peket på ved * ptr.
int i = 1 + 2 * * ptr;
Precedence | Operator | Betydning |
---|---|---|
1 | () (unary) | Invoke en funksjon |
2 | * og -> (unary) | Dereference a pointer |
2 | - (unary) | Returnerer det negative av argumentet |
3 | ++ (unary) | Økning |
3 > 4 | / (binær) | Divisjon |
4 | (binær) | Multiplikasjon |
4 | Modulo | |
5 | + (binær) | Tillegg |
5 | - (binær) | Subtraksjon |
6 | && (binær) | Logisk OG |
6 | ! ! | Logisk ELLER |
7 | =, * =,% =, + =, - = (spesiell) | Oppdragstype |
Evaluer operasjoner med samme forrang fra venstre til høyre (unntatt oppgave, som går den andre veien). | De fleste operatører med samme forrang vurderer fra venstre til høyre. Følgende legger til 1 til 2 og legger resultatet til 3: | int i = 1 + 2 + 3; |
-
Evalueringsordningen for enkelte operatører spiller ingen rolle. For eksempel fungerer tillegget det samme fra venstre mot høyre som det gjør fra høyre til venstre. Evalueringsordningen gir stor forskjell for enkelte operasjoner som divisjon. Følgende deler 8 ved 4 og deler resultatet med 2:
int i = 8/4/2;
Hovedavviket fra denne regelen er oppgave, som vurderes fra høyre til venstre:
a = b = c;
Dette tilordner c til b og resultatet til a.
Evaluer subexpressjoner i ingen bestemt rekkefølge.
Vurder følgende uttrykk:
int i = f () + g () * h ();
-
Multiplikasjon har høyere prioritet, så du kan anta at funksjonene g () og h () kalles før f (), men dette er ikke tilfelle. Funksjonsanrop har høyest forrang for alle, så alle tre funksjonene kalles før enten multiplikasjonen eller tillegget utføres. (Resultatene som returneres fra g () og h () multipliseres og legges deretter til resultatene som returneres fra f ().)
Den eneste tiden som ordren som fungerer kalles, gjør en forskjell, er når funksjonen har bivirkninger for eksempel å åpne en fil eller endre verdien av en global variabel. Du bør definitivt ikke skrive programmene slik at de er avhengige av denne typen bivirkninger.
Utfør bare type konverteringer når det er nødvendig.
Du bør ikke gjøre flere typer konverteringer enn absolutt nødvendig. For eksempel har følgende uttrykk minst tre og muligens fire typer konverteringer:
float f = 'a' + 1;
-
Karet 'a' må fremmes til en int for å utføre tillegget. Int er da omgjort til en dobbel og deretter ned konvertert til en enkelt presisjon flyt. Husk at alt aritmetikk utføres enten i int eller dobbelt. Du bør generelt unngå å utføre aritmetikk på karaktertyper og unngå enkeltpresisjon flyte helt.
5 måter å unngå pekerproblemer i C ++
I C ++ er en
peker
en variabel som inneholder adressen til et objekt i datamaskinens interne minne. Bruk disse trinnene for å unngå problemer med pekere i C ++:
Initialiser pekere når de er deklarert. Forlat aldri pekervariabler uninitialized - ting ville ikke være så ille hvis uninitialized pointers alltid inneholdt tilfeldige verdier - det store flertallet av tilfeldige verdier er ulovlige pekerverdier og vil føre til at programmet krasjer så snart de blir brukt. Problemet er at uinitialiserte variabler har en tendens til å ta på seg verdien av andre tidligere brukte pekervariabler. Disse problemene er svært vanskelig å feilsøke. Hvis du ikke vet hva som er mer å initialisere en peker, skal du initialisere den til nullptr. nullptr er garantert å være en ulovlig adresse.
-
Nullpunktspunkter etter at du har brukt dem.
På samme måte er alltid null en pekervariabel en gang pekeren ikke lenger gyldig ved å tildele den verdien nullptr. Dette er spesielt tilfellet når du returnerer en blokk med minne til bunken ved å slette; nullstill alltid pekeren etter å ha returnert heapminne.
Tillat minne fra bunken og send det tilbake til bunken på samme "nivå" for å unngå hukommelsesslekkasje.
-
Prøv alltid å returnere en minneboks til bunken på samme abstraksjonsnivå som du tildelte den. Dette betyr generelt at du prøver å slette minnet på samme nivå av funksjonsanrop.
Fang et unntak for å slette minne når det er nødvendig.
-
Ikke glem at et unntak kan oppstå nesten hver gang. Hvis du har tenkt å ta unntaket og fortsette å operere (i motsetning til å la programmet krasje), må du sørge for at du unngår unntaket og returnerer noen minneblokker til bunken før poengene som peker på dem, går utenfor omfanget, og minnet er tapt.
Pass på at typene samsvarer nøyaktig.
-
Pass alltid på at pekepunktene stemmer overens med ønsket type. Ikke omdirigere en peker uten noen spesiell grunn. Vurder følgende:
void fn (int * p); void myFunc () {char c = 'a'; char * pC = & c; fn ((int *) pC);}
-
Ovennevnte funksjon kompilerer uten klage siden tegnpekeren pC har blitt omarbeidet til en int * for å matche erklæringen av fn (int *); Men dette programmet vil nesten sikkert ikke fungere. Funksjonen fn () forventer en peker til et fullt 32-biters heltall og ikke noe rinky-dink 8-bit char. Disse typer problemer er svært vanskelig å sortere ut.
Hvordan og når du skal lage dype kopier i C ++
Klasser som tildeler ressurser i deres konstruktør, bør normalt inkludere en kopiekonstruksjon for å lage kopier av disse ressursene. Fordeling av en ny blokk med minne og kopiering av innholdet i originalen til denne nye blokken kalles å opprette en
dyp kopi
(i motsetning til standard grunne kopi). Bruk følgende trinn for å bestemme hvordan og når du skal lage dype kopier i C ++:
Gjør alltid en dyp kopi dersom konstruktøren allokerer ressurser. Som standard gjør C ++ såkalte "grunne" medlemsbevis av objekter når de overføres til funksjoner eller som resultat av en oppgave. Du må erstatte standard grunne kopi operatører med deres dyp kopi ekvivalent for enhver klasse som tildeler ressurser i konstruktøren. Den vanligste ressursen som blir tildelt, er heapminne som returneres av den nye operatøren. Inkluder alltid en destructor for en klasse som tildeler ressurser.
-
Hvis du lager en konstruktør som tildeler ressurser, må du opprette en destructor som gjenoppretter dem. Ingen unntak.
Alltid erklære destruktoren virtuell.
-
En vanlig nybegynnerfeil er å glemme å erklære din destructor virtuell. Programmet vil gå bra til noen intetanende programmør kommer sammen og arver fra klassen din. Programmet ser fremdeles ut til å fungere, men fordi destruktoren i baseklassen kanskje ikke påberopes på riktig måte, lekker hukommelsen fra programmet som en sikt til det til slutt krasjer. Dette problemet er vanskelig å finne.
Inkluder alltid en kopiekonstruktor for en klasse som tildeler ressurser.
-
Kopikonstruktøren lager en riktig kopi av det nåværende objektet ved å tildele minne fra bunken og kopiere innholdet i kildeobjektet.
Overstyr alltid oppdragsoperatøren for en klasse som tildeler ressurser.
-
Programmører bør motløses fra overordnede operatører, men oppdragsoperatøren er et unntak. Du bør overstyre oppdragsoperatøren for enhver klasse som tildeler ressurser i konstruktøren.
Oppdragsoperatøren bør gjøre tre ting:
-
Pass på at venstre og høyre gjenstand ikke er det samme objektet. Med andre ord, vær sikker på at applikasjonsprogrammereren ikke skrev noe som (a = a). Hvis de er, gjør ingenting.
Oppgi samme kode som destructoren på venstreobjektet for å returnere sine ressurser.
Oppgi samme kode som kopikontaktor for å lage en dyp kopi av høyre gjenstand i venstreobjektet.
-
Hvis du ikke kan gjøre det, må du slette kopiekonstruktøren og oppdragsoperatøren slik at programmet ikke kan lage kopier av objektet.
-
Hvis du ikke engang kan gjøre det fordi kompilatoren din ikke støtter C ++ 2011 slette konstruktørfunksjonen, opprett en tom kopikonstruktør og oppdragsoperatør og erklære dem beskyttet for å holde andre klasser fra å bruke dem.
-
