C++Komma igång med C ++


Anmärkningar

Programmet 'Hello World' är ett vanligt exempel som helt enkelt kan användas för att kontrollera närvaron av kompilator och bibliotek. Den använder C ++ standardbiblioteket, med std::cout från <iostream> , och har bara en fil att kompilera, vilket minimerar risken för eventuellt användarfel under sammanställningen.


Processen för att sammanställa ett C ++ -program skiljer sig i sig mellan kompilatorer och operativsystem. Ämnet Kompilering och byggnad innehåller detaljer om hur man sammanställer C ++ -kod på olika plattformar för olika kompilatorer.

versioner

Version Standard Utgivningsdatum
C ++ 98 ISO / IEC 14882: 1998 1998-09-01
C ++ 03 ISO / IEC 14882: 2003 2003-10-16
C ++ 11 ISO / IEC 14882: 2011 2011-09-01
C ++ 14 ISO / IEC 14882: 2014 2014/12/15
C ++ 17 TBD 2017/01/01
C ++ 20 TBD 2020/01/01

Hej världen

Detta program skriver ut Hello World! till standardutflödet:

#include <iostream>

int main()
{
    std::cout << "Hello World!" << std::endl;
}
 

Se den live på Coliru .

Analys

Låt oss granska varje del av denna kod i detalj:

  • #include <iostream> är ett förbehandlingsdirektiv som innehåller innehållet i standard C ++ headerfil iostream .

    iostream är en standardbibliotekshuvudfil som innehåller definitioner av standardinmatnings- och utgångsströmmar. Dessa definitioner ingår i std namnområdet, som förklaras nedan.

    Standardinmatningar / utgångar (I / O) -strömmar ger sätt för program att få inmatning från och ut till ett externt system - vanligtvis terminalen.

  • int main() { ... } definierar en ny funktion som heter main . Av konvention den main är funktionen uppmanas genomförandet av programmet. Det måste finnas en enda main i en C ++ program, och det måste alltid återgå ett antal av int typen.

    Här är int det som kallas funktionens returtyp . Värdet som returneras av main är en exit-kod.

    Enligt konvention tolkas en programutgångskod på 0 eller EXIT_SUCCESS som framgång av ett system som kör programmet. Alla andra returkoder är associerade med ett fel.

    Om ingen return uttalande är närvarande main (och därmed själva programmet) returnerar 0 standard. I det här exemplet behöver vi inte uttryckligen skriva return 0; .

    Alla andra funktioner, utom de som returnerar void , måste uttryckligen returnera ett värde enligt deras returtyp, annars får inte returnera alls.

  • std::cout << "Hello World!" << std::endl; trycker "Hello World!" till standardutflödet:

    • std är ett namnutrymme , och :: är operatören för omfattningsupplösning som tillåter sökningar för objekt med namn inom ett namnområde.

      Det finns många namnutrymmen. Här använder vi :: att visa att vi vill använda cout från std namnområdet. Mer information finns i Scope Resolution Operator - Microsoft Documentation .

    • std::cout är standardutgångsströmobjektet , definierat i iostream , och det skrivs ut till standardutgången ( stdout ).

    • << är i detta sammanhang ströminföringsoperatören , så kallad eftersom den sätter in ett objekt i strömobjektet .

      Standardbiblioteket definierar << operatören för att utföra datainsamling för vissa datatyper i utgångsströmmar. stream << content sätter in content i strömmen och returnerar samma men uppdaterade ström. Detta gör att ströminföringar kan kedjas: std::cout << "Foo" << " Bar"; skriver ut "FooBar" på konsolen.

    • "Hello World!" är en bokstavstecken , eller en "bokstavlig text." Ströminföringsoperatören för bokstäver för teckensträngar definieras i fil iostream .

    • std::endl är ett speciellt I / O-strömmanipuleringsobjekt , också definierat i fil iostream . Om du sätter in en manipulator i en ström ändras strömtillståndet.

      Strömmanipulatorn std::endl gör två saker: först sätter den in std::endl och sedan spolar den strömbufferten för att tvinga texten att dyka upp på konsolen. Detta säkerställer att de data som infogats i strömmen verkligen visas på din konsol. (Strömdata lagras vanligtvis i en buffert och "spolas" sedan i grupper om du inte tvingar en spolning omedelbart.)

      En alternativ metod som undviker flush är:

      std::cout << "Hello World!\n";
       

      där \n är karaktärsekvensen för det nya linjetecknet.

    • Semikolon ( ; ) meddelar kompilatorn att ett uttalande har avslutats. Alla C ++ - uttalanden och klassdefinitioner kräver en slut / avslutande semikolon.

kommentarer

En kommentar är ett sätt att placera godtycklig text i källkoden utan att C ++ -kompilatorn tolkar den med någon funktionell betydelse. Kommentarer används för att ge insikt i programmets design eller metod.

Det finns två typer av kommentarer i C ++:

Enlinjekommentarer

Den dubbla fram-snedstreck-sekvensen // kommer att markera all text tills en ny linje som en kommentar:

int main()
{
   // This is a single-line comment.
   int a;  // this also is a single-line comment
   int i;  // this is another single-line comment
}
 

C-stil / blockera kommentarer

Sekvensen /* används för att förklara början av kommentarblocket och sekvensen */ används för att förklara slutet på kommentaren. All text mellan start- och slutsekvenserna tolkas som en kommentar, även om texten annars är giltig C ++ -syntax. Dessa kallas ibland "C-stil" -kommentarer, eftersom denna syntax för kommentarer ärvs från C ++: s föregångsspråk, C:

int main()
{
   /*
    *  This is a block comment.
    */
   int a;
}
 

I alla blockkommentarer kan du skriva vad du vill. När kompilatorn möter symbolen */ avslutar den blockkommentaren:

int main()
{
   /* A block comment with the symbol /*
      Note that the compiler is not affected by the second /*
      however, once the end-block-comment symbol is reached,
      the comment ends.
   */
   int a;
}
 

Ovanstående exempel är giltig C ++ (och C) -kod. Att ha ytterligare /* i en blockkommentar kan dock leda till en varning för vissa kompilatorer.

Blockera kommentarer kan också starta och sluta inom en enda rad. Till exempel:

void SomeFunction(/* argument 1 */ int a, /* argument 2 */ int b);
 

Betydelsen av kommentarer

Som med alla programmeringsspråk ger kommentarer flera fördelar:

  • Explicit dokumentation av kod för att göra det lättare att läsa / underhålla
  • Förklaring av kodens syfte och funktionalitet
  • Detaljer om historiken eller resonemanget bakom koden
  • Placering av upphovsrätt / licenser, projektanteckningar, särskilt tack, bidragsgivare, etc. direkt i källkoden.

Men kommentarer har också sina nackdelar:

  • De måste upprätthållas för att återspegla eventuella förändringar i koden
  • Överdriven kommentarer tenderar att göra koden mindre läsbar

Behovet av kommentarer kan minskas genom att skriva tydlig, självdokumenterande kod. Ett enkelt exempel är användningen av förklarande namn för variabler, funktioner och typer. Att fakturera ut logiskt relaterade uppgifter i diskreta funktioner går hand i hand med detta.

Kommentarmarkörer som används för att inaktivera kod

Under utvecklingen kan kommentarer också användas för att snabbt inaktivera delar av koden utan att ta bort den. Detta är ofta användbart för test- eller felsökningsändamål, men är inte bra för något annat än tillfälliga redigeringar. Detta kallas ofta “kommentera”.

På liknande sätt rymmer gamla versioner av ett kodstycke i en kommentar för referensändamål, eftersom det rör sig om filer medan det ger lite värde jämfört med att utforska kodens historia via ett versioneringssystem.

Fungera

En funktion är en kodenhet som representerar en sekvens av uttalanden.

Funktioner kan acceptera argument eller värden och returnera ett enda värde (eller inte). För att använda en funktion används ett funktionssamtal på argumentvärden och användningen av själva funktionssamtalet ersätts med dess returvärde.

Varje funktion har en typsignatur - typen av dess argument och typen av dess returtyp.

Funktioner är inspirerade av begreppen procedur och matematisk funktion.

  • Obs: C ++ -funktioner är i huvudsak procedurer och följer inte den exakta definitionen eller reglerna för matematiska funktioner.

Funktioner är ofta avsedda att utföra en specifik uppgift. och kan ringas från andra delar av ett program. En funktion måste deklareras och definieras innan den anropas någon annanstans i ett program.

  • Obs! Definitioner av populära funktioner kan vara dolda i andra filer som inkluderas (ofta för bekvämlighet och återanvändning i många filer). Detta är en vanlig användning av rubrikfiler.

Funktionsdeklaration

En funktionsdeklaration förklarar att det finns en funktion med dess namn och typsignatur till kompilatorn. Syntaxen är enligt följande:

int add2(int i); // The function is of the type (int) -> (int)
 

I exemplet ovan förklarar funktionen int add2(int i) följande till kompilatorn:

  • Returtypen är int .
  • Namnet på funktionen är add2 .
  • Antalet argument för funktionen är 1:
    • Det första argumentet är av typen int .
    • Det första argumentet hänvisas till i funktionens innehåll med namnet i .

Argumentnamnet är valfritt; deklarationen för funktionen kan också vara följande:

int add2(int); // Omitting the function arguments' name is also permitted.
 

Enligt endefinitionsregeln kan en funktion med en viss typsignatur endast deklareras eller definieras en gång i en hel C ++ kodbas som är synlig för C ++ kompilatorn. Med andra ord kan funktioner med en specifik typsignatur inte definieras om igen - de måste bara definieras en gång. Följaktligen är följande inte giltigt C ++:

int add2(int i);  // The compiler will note that add2 is a function (int) -> int
int add2(int j);  // As add2 already has a definition of (int) -> int, the compiler
                  // will regard this as an error.
 

Om en funktion returnerar ingenting skrivs dess void som void . Om det inte tar några parametrar bör parametern listan vara tom.

void do_something(); // The function takes no parameters, and does not return anything.
                     // Note that it can still affect variables it has access to.
 

Funktionssamtal

En funktion kan kallas efter att den har deklarerats. Till exempel efterlyser följande program add2 med värdet 2 i funktion main :

#include <iostream>

int add2(int i);    // Declaration of add2

// Note: add2 is still missing a DEFINITION.
// Even though it doesn't appear directly in code,
// add2's definition may be LINKED in from another object file.

int main()
{
    std::cout << add2(2) << "\n";  // add2(2) will be evaluated at this point,
                                   // and the result is printed.
    return 0;  
}
 

Här är add2(2) syntaxen för ett funktionssamtal.

Funktionsdefinition

En funktionsdefinition * liknar en deklaration, förutom att den också innehåller koden som körs när funktionen anropas i dess kropp.

Ett exempel på en funktionsdefinition för add2 kan vara:

int add2(int i)       // Data that is passed into (int i) will be referred to by the name i
{                     // while in the function's curly brackets or "scope."
                    
    int j = i + 2;    // Definition of a variable j as the value of i+2.
    return j;         // Returning or, in essence, substitution of j for a function call to
                      // add2.
}
 

Funktion Överbelastning

Du kan skapa flera funktioner med samma namn men med olika parametrar.

int add2(int i)           // Code contained in this definition will be evaluated
{                         // when add2() is called with one parameter.
    int j = i + 2;
    return j;
}

int add2(int i, int j)    // However, when add2() is called with two parameters, the
{                         // code from the initial declaration will be overloaded,
    int k = i + j + 2 ;   // and the code in this declaration will be evaluated
    return k;             // instead.
}
 

Båda funktionerna kallas med samma namn add2 , men den faktiska funktionen som kallas beror direkt på mängden och typen av parametrar i samtalet. I de flesta fall kan C ++ -kompileraren beräkna vilken funktion som ska ringas. I vissa fall måste typen anges uttryckligen.

Standardparametrar

Standardvärden för funktionsparametrar kan endast anges i funktionsdeklarationer.

int multiply(int a, int b = 7); // b has default value of 7.
int multiply(int a, int b)
{
    return a * b;               // If multiply() is called with one parameter, the
}                               // value will be multiplied by the default, 7.
 

I det här exemplet kan multiply() kallas med en eller två parametrar. Om endast en parameter anges har b ett standardvärde på 7. Standardargument måste placeras i de senare argumenten för funktionen. Till exempel:

int multiply(int a = 10, int b = 20); // This is legal 
int multiply(int a = 10, int b);      // This is illegal since int a is in the former
 

Specialfunktionssamtal - operatörer

Det finns specialfunktionssamtal i C ++ som har annan syntax än name_of_function(value1, value2, value3) . Det vanligaste exemplet är operatörernas.

Vissa specialteckensekvenser som kommer att reduceras till funktionssamtal av kompilatorn, till exempel ! , + , - , * , % och << och många fler. Dessa specialtecken är vanligtvis associerade med icke-programmeringsanvändning eller används för estetik (t.ex. + -tecknet känns vanligtvis tilläggssymbol både inom C ++ -programmering och i elementär matematik).

C ++ hanterar dessa karaktärsekvenser med en speciell syntax; men i huvudsak reduceras varje operatör av en operatör till ett funktionssamtal. Till exempel följande C ++ -uttryck:

3+3
 

motsvarar följande funktionssamtal:

operator+(3, 3)
 

Alla operatörsfunktionsnamn börjar med operator .

Medan C ++: s omedelbara föregångare, C, kan operatörens funktionsnamn inte tilldelas olika betydelser genom att tillhandahålla ytterligare definitioner med olika typsignaturer, i C ++ är detta giltigt. "Dölja" ytterligare funktionsdefinitioner under ett unikt funktionsnamn kallas operatörens överbelastning i C ++, och är en relativt vanlig, men inte universal konvention i C ++.

Preprocessor

Förberedaren är en viktig del av kompilatorn.

Den redigerar källkoden, klipper ut några bitar, ändrar andra och lägger till andra saker.

I källfiler kan vi inkludera förbehandlingsdirektiv. Dessa direktiv berättar förbehandlaren att utföra specifika åtgärder. Ett direktiv börjar med ett # på en ny linje. Exempel:

#define ZERO 0
 

Det första förbehandlingsdirektivet du kommer att uppfylla är antagligen

#include <something>
 

direktiv. Vad den gör är tar alla something och infogar det i filen där direktivet var. Hejvärldsprogrammet börjar med linjen

#include <iostream>
 

Den här raden lägger till de funktioner och objekt som låter dig använda standardinmatningen och utgången.

C-språket, som också använder förbehandlaren, har inte så många rubrikfiler som C ++ -språket, men i C ++ kan du använda alla C-rubrikfiler.


Nästa viktiga direktiv är antagligen

#define something something_else
 

direktiv. Detta berättar förbehandlaren att när den går längs filen måste den ersätta varje förekomst av something med something_else . Det kan också göra saker som liknar funktioner, men som förmodligen räknas som avancerad C ++.

something_else behövs inte, men om du definierar something som ingenting, utanför förbehandlingsdirektiven, kommer alla händelser av something att försvinna.

Detta är faktiskt användbart på grund av #if , #else och #ifdef . Formatet för dessa skulle vara följande:

#if something==true
//code
#else
//more code
#endif

#ifdef thing_that_you_want_to_know_if_is_defined
//code
#endif
 

Dessa direktiv sätter in koden som finns i den sanna biten och raderar de falska bitarna. detta kan användas för att ha kodbitar som endast ingår i vissa operativsystem utan att behöva skriva om hela koden.

Standard C ++ -kompileringsprocessen

Exekverbar C ++ -programkod produceras vanligtvis av en kompilator.

En kompilator är ett program som översätter kod från ett programmeringsspråk till en annan form som är (mer) direkt körbar för en dator. Att använda en kompilator för att översätta kod kallas kompilering.

C ++ ärver formen för sin sammanställningsprocess från sitt "överordnade" språk, C. Nedan visas en lista som visar de fyra huvudstegen i sammanställningen i C ++:

  1. C ++-förbehandlaren kopierar innehållet i alla inkluderade rubrikfiler till källkodfilen, genererar makrokod och ersätter symbolkonstanter definierade med #define med sina värden.
  2. Den utvidgade källkodefilen som produceras av C ++ förbehandlaren sammanställs till monteringsspråk som är lämpligt för plattformen.
  3. Monteringskoden som genereras av kompilatorn samlas i lämplig objektkod för plattformen.
  4. Objektkodfilen som genereras av monteraren är länkad till objektkodfilerna för alla biblioteksfunktioner som används för att producera en körbar fil.
  • Obs: en del sammanställd kod är länkad ihop, men inte för att skapa ett slutligt program. Vanligtvis kan den "länkade" koden också paketeras i ett format som kan användas av andra program. Detta "paket med paketerade, användbar kod" är vad C ++ -programmerare kallar ett bibliotek.

Många C ++ -kompilatorer kan också slå samman eller avfoga vissa delar av kompilationsprocessen för att underlätta eller för ytterligare analys. Många C ++ -programmerare kommer att använda olika verktyg, men alla verktyg följer i allmänhet denna generaliserade process när de är involverade i produktionen av ett program.

Länken nedan utvidgar denna diskussion och ger en fin grafik som hjälp. [1]: http://faculty.cs.niu.edu/~mcmahon/CS241/Notes/compile.html

Synlighet av funktionsprototyper och förklaringar

I C ++ måste kod deklareras eller definieras före användning. Till exempel ger följande ett kompileringstidsfel:

int main()
{
  foo(2); // error: foo is called, but has not yet been declared
}

void foo(int x) // this later definition is not known in main
{
}
 

Det finns två sätt att lösa detta: att lägga antingen definitionen eller deklarationen av foo() före dess användning i main() . Här är ett exempel:

void foo(int x) {}  //Declare the foo function and body first

int main()
{
  foo(2); // OK: foo is completely defined beforehand, so it can be called here.
}
 

Det är emellertid också möjligt att "framlänga" funktionen genom att bara lägga en "prototyp" -deklaration före dess användning och sedan definiera funktionskroppen senare:

void foo(int);  // Prototype declaration of foo, seen by main
                // Must specify return type, name, and argument list types
int main()
{
  foo(2); // OK: foo is known, called even though its body is not yet defined
}

void foo(int x) //Must match the prototype
{
    // Define body of foo here
}
 

Prototypen måste ange returtyp ( void ), namnet på funktionen ( foo ) och argumentlistans variabeltyper ( int ), men namnen på argumenten behövs INTE .

Ett vanligt sätt att integrera detta i organisationen av källfiler är att skapa en rubrikfil som innehåller alla prototypdeklarationer:

// foo.h
void foo(int); // prototype declaration
 

och ge sedan den fullständiga definitionen någon annanstans:

// foo.cpp --> foo.o
#include "foo.h" // foo's prototype declaration is "hidden" in here
void foo(int x) { } // foo's body definition
 

och koppla sedan motsvarande objektfil foo.o den har sammanställts till den sammanställda objektfilen där den används i main.o , main.o :

// main.cpp --> main.o
#include "foo.h" // foo's prototype declaration is "hidden" in here
int main() { foo(2); } // foo is valid to call because its prototype declaration was beforehand.
// the prototype and body definitions of foo are linked through the object files
 

En ”olöst extern symbol” inträffar när funktionen prototyp och samtals finns, men funktionen kroppen inte är definierad. Dessa kan vara svårare att lösa eftersom kompilatorn inte kommer att rapportera felet förrän det sista länkningssteget och det vet inte vilken rad som ska hoppa till i koden för att visa felet.