Talen en automaten/2011-12/producten/Cplusplus

Uit Werkplaats
Ga naar: navigatie, zoeken

Page Break




Algolfrag.jpg

Talen en automaten

Erik Barendsen


 © comments



C++

Julian Neeleman, Maarten Schellevis



Talen en automaten 2011-12



14:00 B






Julian Neeleman Informatica
 e-mail 

cursuspagina


Maarten Schellevis
 e-mail 

cursuspagina


Page Break





Beschrijving

In 'The Design and Evolution of C++' (1994) geeft Bjarne Stroustrup - de ontwerper van C++ - een aantal richtlijnen die hij gebruikte bij het ontwerpen van C++. Het moest een statisch getypeerde taal worden, met de efficiëntie en portabiliteit van C. Daarnaast is het ontworpen om verschillende programmeerstijlen te ondersteunen, zoals object-geörienteerd- en generiek programmeren en het is bijna een volledige superset van C, zijn voorganger (er zijn uitzonderingen). C++ staat op zichzelf en is niet gebonden aan een platform of programmeeromgeving. Er is in C++ ook een mogelijkheid om gebruik te maken van library's, dit zijn verzamelingen typen en functies die je kan gebruiken nadat je ze in de sourcecode hebt geimporteerd of je kan de functies gebruiken als je de betreffende library aangeeft. Deze library's zijn een zeer grote uitbreiding voor C++. Er is een standaard library bestaande uit verschillend gedeelten waaronder input en output. Ook zijn er veel andere librarys die niet voldoen aan de C++ standaard waarvan sommige zelfs in een andere taal geschreven zijn! C++ wordt gekenmerkt als een efficiënte taal met brede mogelijkheden en een uitgebreide syntax. Het is bedoeld als een verbeterde versie van C, die een aantal moeilijkheden van zijn voorganger wegneemt.


Operator overloading

In tegenstelling tot C bestaat in C++ de mogelijkheid tot het "overloaden" van de "operators". Operatoren is het nederlandse woord voor "operators", een operator is een definitie van een bewerking tussen twee objecten. voorbeelden hiervan zijn "5 maal 3" wat het zelfde betekent als 5 maal het getal 3 optellen bij het getal 0 of "5 tot de macht 2" wat hetzelfde is als 5*5. Het overloaden van een operator in C++ houdt in dat er een nieuwe betekenis wordt gegeven voor een operatie van tussen 2 variabelen/objecten van een specifiek type. Dit maakt C++ zeer flexibel en een stuk compacter deze operator overloading kan namelijk ook worden gebruikt in een specifiek gedeelte van je code.


Templates

Een template is een sjabloon voor een stuk code. Met behulp van templates is het mogelijk een stuk broncode te generaliseren, zodat het voor meer object types bruikbaar is. C++ heeft een beter-uitgerust type systeem en template-ondersteuning dan C. De C++ implementatie voor een template voorkomt veelvoorkomende programmeerfouten zoals het toevoegen van een verkeerd element aan de rij. Ook staat het beter compiler optimalisatie toe. Een voorbeeld van twee stukjes code met gelijke functie uit een artikel van Jakob Østergaard "C versus C++" (2004):

C:

  struct element_t {
     struct element_t *next, *prev;
     void *element;
  };

C++:

  template <typename T>
  struct element_t {
     element_t<T> *next, *prev;
     T element;
  };

Vergelijking met Python

Er wordt wel een prijs betaald voor de brede functionaliteit van C++; de syntax is zeer uitgebreid en types moeten zeer nauwkeurig worden aangegeven. In Python wordt herkend of iets een integer, een double of een character is, maar bij het declaren van een variabele in C++ moet dit worden aangegeven. Daarnaast is Python gevoelig voor het gebruik van tabs, waar C++ brackets gebruikt om scopes aan te duiden. We vergelijken C++ en Python d.m.v. een implementatie van de Fibonaccirij.

C++:

  long fib(unsigned long n)
  {
     int a = 0;
     int b = 1;
     for (int i = 0; i < n; i++)
     {
        cout << a;
        int help = a;
        a = b;
        b = help + b;
     }
  }

Python:

  def fibonacci (n):
    a, b = 0, 1
    for i in range(0, n):
      print a
      a, b = b, a + b

N.B.: in C++ is het nodig expliciet een 'help' variabele te declaren om tijdelijk a in op te slaan. Python past de waardes niet alleen aan in één regel, het maakt ook uit zichzelf een 'help' variabele en de programmeur hoeft hier niet over na te denken.

De uitgebreide syntax heeft ook voordelen. Het zorgt ervoor dat beginnende programmeurs beter doorhebben wat ze doen (het neemt geen werk uit handen) en het maakt code vaak veel beter leesbaar. Het maakt C++ ook niet trager, want Python heeft net zo goed een 'help' variabele nodig om zijn bewerkingen uit te voeren, echter ziet de programmeur deze variabele nooit.

Regulier?

Te Bewijzen: De programmeertaal C++ is niet regulier.

Stel C++ is wel regulier en de automaat van C++ heeft k states. Het pomplemma voor reguliere talen zegt dat een woord met een lengte groter dan k pompbaar is in de eerste k symbolen van het woord en dan nog steeds aan de eigenschappen van de taal voldoet.

De syntax van C++ zegt dat elke openingsbracket "{" ook gesloten moet worden met een sluitbracket "}". Hieronder is een fragment C++ code waarbij de eerste k symbolen openingsbrackets zijn. Volgens het pomplemma zijn deze k symbolen opgebouwd uit 3 delen: u, v en w. Waarbij u bestaat uit een aantal symbolen voorgaand aan v, v het pompbare deel bestaat uit minimaal een symbool en w bestaat uit een aantal symbolen achter v. In dit voorbeeld kan v alleen maar bestaan uit een aantal openingsbrackets omdat er in k alleen maar openingsbrackets zijn. Gaan we v pompen dan neemt het aantal openingsbrackets toe terwijl het aantal sluitbrackets gelijk blijft waardoor er meer openingsbrackets dan sluitbrackets zijn en het dus geen geldige C++ code meer is.


<source lang="c"> {

{
 ...
    {
     std::cout<<"Hello World!";
    }
 ...
}

}

</source>

C++ is dus niet pompbaar en dus geen reguliere taal.

Contextvrij?

Voor C++ bestaat, zoals voor de meeste talen, een grammatica, die onder andere hier te vinden is: http://www.csci.csusb.edu/dick/c++std/cd2/gram.html . Deze grammatica beschrijft in principe de hele taal, echter bestaat er in C++ een mogelijkheid tot ambiguïteit tussen expressies en declaraties, zoals te zien is in het volgende voorbeeld:

  type_spec(*i)(int);         // declaratie
  type_spec(j)[5];            // declaratie
  type_spec(m) = { 1, 2 };    // declaratie
  type_spec(*k) (float(3));   // declaratie

Omdat de compiler in deze gevallen niet uit de syntax kan opmaken of het een expressie of een declaratie betreft, kan men zeggen dat C++ een niet-contextvrije grammatica heeft. Echter, in dergelijke situaties bepaalt men automatisch dat het een declaratie betreft. De laatste functie geeft daarom een compiler error, omdat dit eigenlijk een expressie is.

De meningen over of dit een reden is om C++ te beschouwen als een niet-contextvrije grammatica lopen uiteen, maar het is op zijn minst een taal met ambiguïteit tussen expressies en declaraties.

Hoewel de uitgebreide grammatica van C++ te complex is om in zijn volledigheid te analyseren, kunnen we wel een simplificatie hiervan maken die beschrijft hoe een functie wordt opgebouwd en hoe een for-loop kan worden gebruikt. Deze grammatica ziet er als volgt uit:

  S -> Type Naam L Parameters R OpenBracket Inhoud SluitBracket S | lambda
  Type -> typenaam // bijvoorbeeld int, char of bool
  Naam -> identificatie // zelfgekozen naam voor de variabele (bijv. hond, number of woord)
  L -> '('
  R -> ')'
  Parameters -> Type Naam Parameters | lambda // maakt nieuwe parameters aan
  OpenBracket -> '{'
  SluitBracket -> '}'
  Inhoud -> Regel Inhoud // maakt nieuwe regels aan 
  Regel -> Type Naam PK // maakt een nieuwe variabele aan
  PK -> ';'
  Regel -> ForLoop OpenBracket Inhoud SluitBracket // maakt een nieuwe for-loop aan
  ForLoop -> 'for ' L Type Naam PK Conditie PK Inhoud R
  Conditie -> ...
  etc.

Uit dit stukje grammatica kan worden opgemaakt dat de structuur van C++ zo is ingedeeld dat elke scope een opening en een sluiting heeft met daartussen de inhoud van de scope. Dit is een contextvrije vorm.