De programmeertaal C++

© 2010-2015 Hein Pragt

Op deze pagina probeer ik een soort naslagwerk voor 'C++' programmeurs te maken, in het Nederlands. In dit artikel probeer ik ook een aantal goede redenen te geven om over te stappen op 'C++' zonder het object georiënteerd deel te gebruiken. Dit is in mijn visie de eerste stap die 'C' programmeurs kunnen maken zonder een cultuur schok door te maken. Object georiënteerd programmeren is namelijk een andere denk en ontwerp wijze in plaats van alleen wat nieuwe functies. Maar 'C++' is heel goed te gebruiken zonder object georiënteerd te werken. Toen ik in 1986 voor het eerst kennismaakte met 'C++' zei ik spottend tegen een collega, "Alles wat in 'C++' mogelijk is, kan ik ook in standaard 'C' maken, ik zie het nut niet zo". Ik had volkomen gelijk, en vele collega 'C' programmeurs waren het met me eens. Het was pas twee jaar later toen ik helemaal 'om' was voor het object georiënteerd programmeren, dat ik mij verder ging verdiepen in 'C++'. Ik ontdekte ook dat zonder object georiënteerd programmeren de taal 'C++' een sterk verbeterde 'C' is, en ik zal proberen uit te leggen waarom.


Hele strikte type controle

De controle op variabele en functie types is heel strikt in 'C++', dit houdt in dat als U alles heel goed moet declareren. Als U een ander type parameter wilt gebruiken dan de gedeclareerde, of in een expressie verschillende data types gebruikt, bent U verplicht een cast te gebruiken. Dit geld ook voor signed en unsigned en char naar int conversie. Zo slordig als we in 'C' kunnen zijn kan in 'C++' absoluut niet. Dit lijkt wat omslachtig maar het zorgt ervoor dat we beter aangeven aan de compiler wat we eigelijk willen, en voorkomt veel programmeer fouten.

Ik vind het prettig om goed door een compiler op mijn vingers gekeken te worden, en compileer dan ook meestal op het hoogste warning niveau. Dit is gratis controle door de compiler, en dit heeft me al redelijk wat bugs bespaart. Helaas is het met Microsoft nog wel eens zo dat hun eigen include files niet zonder warning op het hoogste warning niveau gecompileerd kunnen worden. Dit los ik dan op door rond de #include file een #pragma te gebruiken om de warnings tijdelijk uit te zetten.


Verbeterd commentaar

'C++' heeft een verbeterde methode om commentaar te plaatsen. Naast de bestaande methode door middel van /* ... */ staat C++ het ook toe, door middel van een // dubbele slash de rest van de regel als commentaar te beschouwen. (Dit was in de meeste ANSI compilers ook al mogelijk.)


Andere cast methode

'C++' kent een verbeterde cast, in plaats van (long) var kan nu ook long(var) geschreven worden, wat een stuk duidelijker aangeeft wat we bedoelen.


result = long(var1) * var2;

TestFunc(long(IntVar),Str); 


Verplichte functie declaraties

In 'C++' moet elke functie gedeclareerd worden voor ze gebruikt word, dit verplicht de programmeur tot netjes declareren. Ook dit is weer extra controle door de compiler van de programmeur. Als deze de functie anders wil aanroepen, is hij verplicht een cast te gebruiken. (Dit was in ansi 'C' ook al aanbevolen maar niet verplicht.)


Functie overloading

Binnen 'C++' word behalve de functie naam ook de parameters en de return waarde gebruikt in de symbol table. Dit houdt in dat we dezelfde functie met andere parameters kunnen declareren zonder iedere keer een andere naam te bedenken. Denk hierbij aan de functies atoi() atof() atol() uit de standaard 'C' library, welke in 'C++' een functie kunnen zijn.

In 'C' zouden we het afdrukken van verschillende types zo oplossen:


void DrukafInt(int getal)
{
  // Code
}
void DrukafLong(long getal)
{
  // Code
}
void DrukafStr(char *str)
{
  // Code 
}

void TestFunc()
{
  DrukafInt(10);
  DrukafLong(423424L);
  DrukafStr("test");
}
In 'C++' zoekt de compiler de goede functie afhankelijk van de parameters.

void Drukaf(int getal)
{
  // Code
}
void Drukaf(long getal)
{
  // Code
}
void Drukaf(char *str)
{
  // Code
}

void TestFunc()
{
  Drukaf(10);
  Drukaf(423424L);
  Drukaf("test");
}

Dit houdt in dat we voor alle parameter types een functie naam kunnen gebruiken.


Default parameters

In 'C++' kunnen we een functie met een variabel aantal parameters aanroepen, als we gebruik maken van default parameters.


void TestFunc(char *Str, int i = 5)
{
  // Druk resultaat af!
  cout << Str << " " << i;   
}

// Dit drukt 10 af
TestFunc("Dit is met twee parameters",10);
TestFunc("Dit is // Dit drukt 5 af


Meer vrijheid in het declareren van variabelen

In 'C++' kunnen variabelen overal gedeclareerd worden, en hun scope is het block {} waarbinnen ze zich bevinden. In 'C' moeten declaraties vooraf gaan aan statements, in 'C++' mag daar waar een statement toegestaan is, ook een declaratie staan. Dit maakt het mogelijk om bijvoorbeeld binnen if code blokken locale variabelen te gebruiken.


int TestFunc()
{
int i;  
// Deze variabele is globaal voor de hele functie

  // code
  if (var1> 100) { 
    int y;
  } // Hier stopt de scope van y


Verbeterd FOR statement

In 'C++' kunnen we de loop variabele in het for statement declareren. De scope van deze variabele is het vorige {} code block, hoewel we misschien verwachten dat de scope de for loop zou zijn. In dit voorbeeld is de variabele ind na de for loop ook nog te gebruiken.

void TestFunc()
{
  for (int ind=0; ind < 10; ind++)
  {
    // code
  }
} 


Eenvoudigere struct, enum en union notatie

In 'C++' kunnen we als we een structure gedeclareerd hebben, variabelen van dit soort zonder het woord struct declareren. Dit kon in 'C' ook wel door middel van een typedef, maar is in 'C++' prettiger opgelost.


struct Stype ;

Stype StructVar;
Dit geld ook voor enum en union.

enum Boolean ;

Boolean Gereed = FALSE;


Echte constanten

In 'C++' maakt het keyword const een variabele een echte constante. De compiler zal ook een foutmelding geven als de programmeur de waarde probeert te veranderen.


const int Cvar = 100;

void Test(const int index)
{
  // Hier geeft de compiler een foutmelding
  index = 5;
}

void Test()
{
// Ook hier geeft de compiler een foutmelding
  Cvar = 10; 
} 

// Dit zou in 'C' een foutmelding geven.
int Varray[Cvar]; 

Nieuwe input en output methode

Een van de belangrijkste wijzigingen in 'C++' is de vervanging van de standaard I/O library stdio in iostream. Het gebruik van iostream is heel wat eenvoudiger dan stdio, al is het even wennen voor de doorgewinterde 'C' programmeur. Hier volgen enkele voorbeelden:


// Output Hello world met een newline
cout << "Hello world\n";
// Idem
cout << "Hello world" << endl;

int   VarInt = 4;
int   Var1,Var2;
float VarFloat = 3.14;
char *Str = "Hello";

cout << Str << VarInt << VarFloat << endl;   

// Resulteert in Hello43.14

cout << setw(6) << setfil('*') <<



// Leest 1 integer uit de input stream

cin >> Var1 >> Var2;    

// Leest 2 integers uit de input stream

In 'C' schrijven we vaak voor standaard I/O:


  while (var1 = getchar())
    putchar(var1);
Dit schrijven we in 'C++' als:

  while(cin >> var1)
    cout << var1; 


Nieuwe geheugen beheer methode

In 'C++' kunnen we nu in plaats van de zeer omslachtige en overloaded functies alloc() en free() waarbij we zelf de lengte van objecten moesten berekenen, nu ook new en delete gebruiken. Dit hebben ze duidelijk van de taal Pascal geleend, en werkt heel prettig en efficiënt.


void TestFunc()
{
int *IntPointer;

  IntPointer = new int;
  *IntPointer = 12;
  cout << *IntPointer;
  delete IntPointer;
} 


void TestFunc()
{
int *IntPointer;

  IntPointer = new int(100);
  IntPointer(10) = 12;
  cout << IntPointer(10);
  // Let op bij arrays altijd [] gebruiken
  delete [] IntPointer;     
} 
Dit is zeer gemakkelijk in vergelijking met de oude 'C' methode.


Call by reference

In 'C++' kunnen we nu met call by reference werken in plaats van pointers. Dit maakt de code in bepaalde gevallen, heel wat duidelijker.
In 'C' zouden we het volgende schrijven:


void Swap(int *Var1, int *Var2)
{
int Tmp = *Var1;

  *Var1 = *Var2; 
  *var2 = Tmp;
}

void TestFunc()
{
int V1, V2;

// Voorbeeld aanroep functie swap;
  Swap(& V1, & V2);
}

In 'C++' kunnen we dit op de volgende wijze doen:


void Swap(int& Var1, int& Var2)
{
int Tmp = Var1;

  Var1 = Var2;
  var2 = Tmp;
}

void TestFunc()
{
int V1, V2;

 // Voorbeeld aanroep functie Swap in C++
  Swap(V1,V2);    
}



Conclusie

Bij de meeste compilers is het omschakelen van 'C' naar 'C++' simpelweg het veranderen van de extensie van de source file van TEST.C in TEST.CPP waardoor de compiler weet dat de source file een 'C++' file is. Als U dit doet moet U dit voor alle files doen, omdat 'C' code geen 'C++' kan aanroepen. Omgekeerd is wel via een speciale declaratie mogelijk maar ik raad het af 'C' en 'C++' files door elkaar te gebruiken. Verder kan het aanroepen van zelfgeschreven library functies problemen gaan geven, omdat deze verwachten volgens de 'C' conventie aangeroepen te worden.

Omdat 'C' een subset is van 'C++' kan elk 'C' programma ook door een 'C++' compiler gecompileerd worden. Laat U niet afschrikken door het grote aantal warnings, en zet vooral het warning level niet lager om het aantal warnings terug te brengen. Besteed daarentegen eens een tijdje aan het plaatsen van casts en de kans is groot dat U terloops ook nog een programmeer fout ontdekt.

Tot mijn verbazing zijn er nog steeds bedrijven waar code uitgeleverd word, met een flink aantal compiler warnings, omdat de programmeur denkt dat hij weet wat hij doet, en hij vindt dat de compiler te kieskeurig is. In mijn visie is elke warning een mogelijke programmeer fout, en moet weggewerkt worden.


De basis van object georiënteerd programmeren.

(c) Hein Pragt 1999

Voordat ik begin uit te leggen wat object georiënteerd (vanaf nu OO) programmeren is, begin ik met een definitie wat objecten zijn. Objecten zijn alle dingen die wij kunnen bevatten, met duidelijk afgebakende grenzen. Voorbeelden zijn, een punt op het scherm, een pomp, een bankrekening, een computer, een mens of een programmeertaal. Twee verschillende objecten zijn duidelijk van elkaar te onderscheiden.

Een object heeft een aantal kenmerken:

  • Een identiteit is de unieke code of naam waarmee we twee ovan elkaar onderscheiden, voorbeelden zijn een boek of een bankrekening.
  • De toestand is de huidige waarde van de interne structeen object, een voorbeeld is een bankrekening met een saldo van 1023 gulden.
  • Het gedrag van een object zijn de diensten die het verlede buitenwereld. In het geval van een bankrekening kan dit het ovan het saldo of het verhogen van het saldo zijn.

De buitenwereld heeft geen directe toegang tot het saldo van de bankrekening, maar kan alleen via de boodschappen (member functies) de huidige toestand opvragen, of iets aan de huidige toestand te veranderen.

Een class is een definitie van een object, waarmee instances (echte objecten) van de class gemaakt kan worden. Een class op zich is geen object, er kunnen objecten mee gemaakt worden. Van een class kunnen we meer dan een object (instances) maken. De class is vergelijkbaar met een gietvorm waar we veel dezelfde beeldjes mee kunnen gieten.
In de class definiëren we een interface. We kunnen alleen met een object communiceren via deze interface. Ook definiëren we in de class de interne data, waarin we de toestand van een object opslaan. Interne data is dus ingekapseld en niet direct benaderbaar door de buitenwereld.
OO software is gebaseerd op deze objecten. Een object kunnen we beschouwen als een "gesloten apparaat" met een vast gedefinieerde interface. We hoeven niet te weten wat er zich in het object bevind, we kennen de interface waarmee we communiceren met het object.

Een voorbeeld is een televisie toestel.
We hoeven niet weten wat er in de T.V. zit, we gebruiken alleen de knoppen en het scherm als interface. Als we een andere zender willen kiezen, veranderen en we niet de afstemkring door binnen in het toestel aan een van de onderdelen te draaien, maar we geven de interface via een knop de opdracht op een andere zender af te stemmen. Via deze knoppen kunnen we ook zenders zoeken en in het geheugen van de T.V. opslaan. Hoe dit intern gebeurt willen we niet weten, en is voor het normaal gebruik van het toestel niet belangrijk. Intern kan dit in verschillende toestellen anders opgelost zijn, vanuit de interface zien we hetzelfde. Deze inkapseling van data en methodes is de basis van OO programmeren.

De interface functies zijn ook weer onder te verdelen in vier soorten:

  • Constructors. Dit is de member functie die gebruikt wordobject een begin toestand te geven. Deze zal bij het creëeen object zorgen voor een voorinstelling van de waardes van interne variabelen.
  • Destructors. Deze member functie ruimt een object netjeshet geheugen, als de levenduur van het object afgelopen is.
  • Modifiers. Dit zijn member functies die iets aan de interne tvan het object veranderen, bijvoorbeeld in het geval van de bankrkan de methode StortBedrag het saldo veranderen.
  • Selectors. Dit zijn member functies die de interne toesteen object terugmelden. Deze zullen niets aan de interne toestand veraDit is dus een read-only bewerking, het mag niets aan de interne tvan het object veranderen.

De constructor en destructor zijn interne functies van het object, de modifier en de selector zijn van buiten het object te gebruiken.

Doordat de buitenwereld geen directe toegang heeft op de interne data van een object, kan via de interface functies bijvoorbeeld validatie plaatsvinden. Als gedefinieerd is dat een bankrekening niet negatief mag zijn kan de member functie VerlaagSaldo een controle uitvoeren of het saldo door deze bewerking niet negatief kan worden. In dit geval kan de member functie een fout afhandeling starten of een foutmelding genereren.
De interface bewaakt dus de interne toestand van een object.

De tegenhanger van OO programmeren is het procedure gericht programmeren. Hier staan de bewerkingen centraal, in tegenstelling van OO programmeren waar de gegevens en hun toestand centraal staan. In OO begint men met een data model, waarna men deze via objecten in categorieën onderbrengt, om daarna de interface en de bewerkingen te definiëren. Dit is duidelijk een andere aanpak, die het ook nodig maakt eerst over het programma en het data model na te denken. Dit in contrast met procedure gericht programmeren waar vaak een globale opzet van een programma word gemaakt, waarna deze procedures uitgewerkt worden. Dit is niet per definitie een slechtere aanpak, maar een andere, die ook zijn voordelen kan hebben.
Ik heb wel eens te horen gekregen, als is een ontwerp op papier aan het maken was, "wanneer begin je nu met het project". Deze mensen dachten dat het inkloppen van code het programmeren is. Dit is echter maar een klein deel van het programmeer werk. Het meeste is ook zonder computer te doen.

Een tweede eigenschap van OO programmeren is overerving.

Als we eenmaal een class gedefinieerd hebben, ligt deze vast. Als er nu een specialisatie plaats moet vinden, kan de reeds ontworpen class als basis dienen voor een nieuwe class. De nieuwe class bevat alle onderdelen van de oorspronkelijke class, met een aantal toevoegingen speciaal voor de nieuwe class. Als we een bankrekening zonder rente gedefinieerd hebben met een groot aantal bewerkingen, kunnen we hiervan een rekening met rente afleiden. De bewerkingen die we kunnen uitvoeren op de oorspronkelijke rekening kunnen we ook uitvoeren op de rekening met rente. Deze rekening heeft echter een paar uitbreidingen t.o.v. de oude rekening, en dat is een rentepercentage variabele, een functie om het rentepercentage op te vragen en een functie om het rentepercentage te veranderen. Ook zal er een functie moeten komen om de rente bij te boeken. De code om het saldo op te vragen en het saldo af te boeken hoeven we niet opnieuw te schrijven.

Dit is een eigenschap van OO programmeren die ik nogal eens uit de hand zie lopen. Een goede class hiërarchie opbouwen is een kunst, en soms zie je een ad-hoc class hiërarchie die ondoorzichtig en niet te onderhouden is.
Het werken met OO is geen garantie voor goede en goed onderhoudbare code. Een paar goede voorbeelden van een class hiërarchie zijn OWL van Inprise en MFC van Microsoft.

OO werken ten opzichte van procedureel programmeren betekend vaak de je veel vrijheden die je had moet opgeven en binnen een strakkere structuur moet werken. Dit voorkomt echter heel veel basis programmeer fouten, zodat het resultaat beter, en beter onderhoudbaar is.
Deze vorm van jezelf de beperkingen van een goede structuur opleggen, schrikt sommige 'C' en assembler programmeurs nog wel eens af. Een collega van mij plaagt me wel eens, als ik weer iets met de bestaande code wil doen waarin niet voorzien is, dat beperkingen opleggen geheel in de stijl van OO programmeren is. Ik probeer dan uit te leggen dat ik vanuit de bestaande code deze functionaliteit niet kan toevoegen, maar dat dit in een OO omgeving eenvoudig te doen zou zijn.

Het grote voordeel van OO programmeren is dat het uitnodigt tot goed gestructureerd werken, en dat je in staat bent goede interfaces te maken. Data hiding is in mijn visie een van de beste onderdelen van OO.


Boeken voor 'C' en 'C++' programmeurs.

Dit is het meest gebruikte en meest betrouwbare boek over C++. Van de Engelstalige edities zijn er in de Benelux meer dan 6000 exemplaren verkocht. Het beschrijft de mogelijkheden van de taal, de standaardbibliotheek en belangrijke ontwerptechnieken in hun onderlinge samenhang worden beschreven. Het is een volledig herschreven editie, gebaseerd op de ANSI/ISO C++-standaard. De auteur is de geestelijk vader van de hogere objectgeorienteerde programmeertaal C++. Dit goed geschreven boek is een volledig herziene versie van een eerdere editie en voldoet aan de normen die in de ANSI/ISO C++ standaard zijn vastgelegd. De doelgroep is de ervaren C++ programmeur die een compleet overzicht wil hebben van de mogelijkheden van de objectgeorienteerde taal C++. Het boek heeft voor dit doeleind meer dan voldoende diepgang en uitgebreidheid. Daarnaast is dit boek geschikt als studiemateriaal voor de beginnende of minder ervaren C++ gebruiker. Het geheel heeft een hoog abstractieniveau, maar met enig doorzettingsvermogen en ervaring in objectgeorienteerd programmeren is het boek prima bruikbaar als cursusmateriaal en als naslagwerk. Het is opgedeeld in vier delen met als titels basisfunctionaliteit, abstractiemechanismen, de standaardbibliotheek en ontwerpen van C++ programma's en is voorzien van een introductie en een aantal nuttige appendices. Met trefwoordenregister. De vertaling is goed.


C++ is een krachtige programmeertaal, die het beginners en overstappers niet gemakkelijk maakt. Dat is echter geen enkel probleem: dit boek helpt u uit de brand! In dit boek leert u in klare taal en met humor alles wat u nodig hebt! De auteur legt alles wat u werkelijk moet weten duidelijk en begrijpelijk uit. We beloven niet dat u in korte tijd een professional kunt worden, maar met dit boek zult u snel over een gedegen basiskennis beschikken. U maakt kennis met het opbouwen van programma´s, u wordt snel vertrouwd gemaakt met objectgeoriënteerd programmeren en u leert hoe u met de debugger werkt. Dat alles wordt in overzichtelijke, kleine stappen uitgelegd. De voorbeelden voldoen aan de standaard ANSI C++ en werken daarom met alle C++-compilers. In het boek worden alle voorbeelden gedemonstreerd met Microsoft Visual C++ voor Windows. Werkt u echter liever met Linux, dan kunt u bijvoorbeeld ook de GNU Compiler Collection (GCC) gebruiken. In dit boek wordt de ontwikkelingsomgeving Visual C++ van Microsoft gebruikt. Niet alle mogelijkheden van C++ worden behandeld. Die onderwerpen, die moeilijk zijn voor beginners, worden overgeslagen. Aan het eind van de meeste hoofdstukken zijn oefeningen opgenomen, waarvan de lezer de uitwerking kan downloaden van het internet. Ieder hoofdstuk behandelt een aantal onderwerpen en probeert op een logische manier de volgorde van de opdrachten aan elkaar te breien. De eerste hoofdstukken geven aan in welke stappen het programma wordt gemaakt en hoe gebruik te maken van compiler, bibliotheken en linken om tot een "runbaar" programma te komen. Wat natuurlijk belangrijk is is het werken met de debugger en hoe eenmaal gemaakte fouten op te sporen en te verhelpen. Het boek eindigt met een index.


Last update: 08-01-2018
 Binnen dit thema



 Meer thema's


 Lees hier de privacyverklaring van deze site.

Disclaimer.

Hoewel de heer Hein Pragt de informatie beschikbaar op deze pagina met grote zorg samenstelt, sluit de heer Pragt alle aansprakelijkheid uit met betrekking tot de informatie die, in welke vorm dan ook, via zijn site wordt aangeboden. Het opnemen van een afbeelding of verwijzing is uitsluitend bedoeld als een mogelijke bron van informatie voor de bezoeker en mag op generlei wijze als instemming, goedkeuring of afkeuring worden uitgelegd, noch kunnen daaraan rechten worden ontleend. Op de artikelen van de heer Pragt op deze Internetsite rust auteursrecht. Overname van informatie (tekst en afbeeldingen) is uitsluitend toegestaan na voorafgaande schriftelijke toestemming van de rechthebbende. Voor vragen over copyright en het gebruik van de informatie op deze site kunt u contact opnemen met: (email: mail@heinpragt.com). Dit is mijn