Skip to main content
Who can help to set up a Maxun One solar bike DIY community?

Samenvatting c++

Published: 21 December 2013
Last updated: 19 April 2023
Content [Show/Hide]

Om het programmeren in c++ eenvoudig te houden kun je het op een procedurele manier gebruiken met alleen de basis functies. Maar als je alles uit te kast trekt dan is c++ zeer ingewikkeld. Ik merk dat het moeilijk is om alle mogelijkheden van c++ paraat te hebben, terwijl dit voor het programmeren toch wel nodig is. Als je een tijdje niet meer met c++ gewerkt hebt dan raak je al snel veel kennis kwijt. Daarom heb ik een overzicht gemaakt van álle mogelijkheden van c++. Het is bedoeld om snel je c++ kennis op te frissen. Het is niet geschikt voor nieuwelingen die de programmeertaal c++ willen leren.

Er wordt speciaal aandacht gegeven aan de gecompliceerde aspecten van c++. In het hoofdstuk tips staan oplossingen voor lastige compiler en linker foutmeldingen.

Dit document was aanvankelijk alleen mijzelf bedoeld. Maar ik vond het zonde om het niet te publiceren. Ik heb geen tijd besteed om het overzicht aan te passen voor het web, het is dus zoals het is. Sommige voorbeelden hebben betrekking op Borland c++ Builder. Een goed Nederlands c++ leerboek is C++ van Leen Ammeraal, hierin worden de onderstaande c++ mogelijkheden uitgebreid uitgelegd.

Reacties zijn welkom, als er iets belangrijks ontbreekt dan hoor ik het graag.

Algemeen

Basis

Enkele nieuwe mogelijkheden in c++

·       Referentie argumenten, pass by reference

·       Commentaar op twee manieren:

/* blabla */

// blabla

·       Case sensitive. Gebruik hoofdletters in variabelen: GetVideoRect;

·       Iostreams: cout << “hello’;

·       Eenvoudigere struct, enum en union notatie: zonder typedef.

struct S {int a, int b};

S StructVar;

Declaratie en definitie

·       Declaratie      

Kenbaar maken van een variabele, functie of klasse (= interface)

int i;

void fun(int);

·       Initialisatie / definitie / implementatie

Waarde toekennen aan object / variabele

i = 123;

of body / code toekennen

Externe declaratie

File1

int g = 1; // declaration and initialisation

File2

extern int g; // to use g in file2

extern classBlabla g; // classes

Expressie en statement

·       Expressie

i=1234

i+5

·       Statement is een expressie met ; ofwel een programma element zoals for.

i=1234;

Handige mogelijkheden

·       Komma operator if(read(i), i==0) // de tweede expressie wordt afgeleverd

·       Increment operator i++ (i wordt afgeleverd) ++i (i+1 wordt afgeleverd)

·       if(i=a+b) // if(expressie) combinatie van test en expressie

·       c = (a>b) ? a : b; Conditional operator ?

Repetitie statements

·       while(variabele) { };

·       do{ }while(variabele);

·       for(int i =0; i <= 10; i++){ }

·       continue hervat de do, for, while loop (én doe de test)

·       break verlaat een do, for, while loop

Control flow statements

·       if(x==0) {}

else {}

·       switch(x)

{ case 1: blabla;

  case 2: { blabla; }

  default:

}

Macro’s

Zorgt voor eenvoudige schrijfwijze voor zeer ingewikkelde notaties:

\ doorgaan volgende regel

·       Token-pasting (merging) operator (##) #define a(x) a##x // a(b) -> ab

·       Stringizing Operator(#) #define c(d) #d // c(a) -> “a”

·       Charizing Operator (#@) #define a(x)  #@x // a(b) -> ‘b‘

·       Variadic macros #define d(args...) int i[]={args} // d(1,2,3) -> int i={1,2,3}

Voorbeelden

#define fun(x,y) ((x) * (y))

#define x(n) printf_s("bla" #n "=%d", bla##n) // x(9) -> printf_s("bla9=%d", bla9);

#define progmemPrintf(x) _progmemPrintf(PSTR(x)) PSTR is heel lang!

#define _HEX(a) Struct_BASED(a, HEX) levert een object af

#define FLASH_STRING(name, value) \

  static const char name##_flash[] PROGMEM = value; \ (meerdere regels, name##

  _FLASH_STRING name(name##_flash); creeer object

#define debug(format, args...) fprintf (stderr, format, args)

Namespaces

·       Verdeling in subsystemen die dezelfde namen van globale variabelen gebruiken, bijvoorbeeld bij gebruik van bibliotheken met conflicterende namen. Zie programma Namespace.

·       Er zijn drie methodes om de namespace te gebruiken:

namespace A

{ char c = 'a';

}

using namespace A; char x = c; // Methode 1

using A::c; char x = c; // Methode 2

char x = A::c; // Methode 3

Run-Time Type information RTTI

·       typeid

Type identificatie:

int i;

String s = typeid(i).name(); // s = ”int”

String s = typeid(*p).name() // Bij pointers

const char* myObjectTypeName = typeid(*this).name(); // Let op: klassenaam, moet met const char*

Type vergelijking met typeid:

if(typeid(i) == typeid(int))

·       dynamic_cast

Zie cast

Variabelen

·       Constant, int, float, enz.

Zie uitvoerig overzicht Borland c++ Builder Help “Constants and internal representation”

int -32768 … 32767

insighed int = unsigned 0 ... 65535

float                                   1.18         10^-38 < |X| < 3.40  10^38                      Scientific (7-digit) precision) max 9.999.999

double             64             2.23  10^-308 < |X| < 1.79  10^308        Scientific (15-digit precision)

long double    80             3.37  10^-4932 < |X| < 1.18  10^4932   Financial (18-digit precision)

·       Variabelen kunnen overal gedeclareerd worden, bv. in for statement: for (int i=0; i < 10; i++)

int x;

for(int x; x<10; x++) // Local scope voor variabelen

{int x;} // Andere variable x dan in regel 1

·       Initialisatie kan op twee manieren:

int x(1); // Methode 1

int x = 1; // Methode 2

·       bool

bool b = false; b = true;

·       Nieuwe integer types: Shortint, Smallint, Longint, Int64, Byte, Word, and Longword.

·       Enum

Doe enumeratie in hoofdletters

Enum {ZONDAG, MAANDAG}; unnamed enum type, ZONDAG=0, MAANDAG=1

enum MyEnumType {ZONDAG, MAANDAG} MyEnumTypeName;

enum {ZONDAG, MAANDAG} MyEnumTypeName; // mag ook

Gebruik: MyEnumTypeName = ZONDAG; int i = ZONDAG;

·       Typedef

Een nieuw type definieren.

typedef char *ptr; // Of typedef char* ptr;

ptr p1; // ptr is nieuw type, n.l. char*

Pointers

·       Plaats van het sterretje:

int *i, j; // Eén pointer, sterretje moet bij i

int* i, j; // Twee pointers, sterretje moet bij int

·       Indirect member operator

p->x = (*p).x

·       Void pointer

Is een pointer naar ieder willekeurig type

·       Functiepointers

Zie Functie pointers

Diverse functies

·       assert(int test) Tests een conditie en evt. aborts program, foutmelding met programmaregelnummer

·       min(10, 20) // = 10 max(20, 10) // = 20

·       Sleep(1000); // Pause 1000 ms

Arrays

Een dimensionale array:

int a[10] = {1, 2, 3}; // a[9] = 0

of int b[] = {1, 2, 3};

Twee dimensionale array:

int c[][3] = {{60, 30, 50}, {20, 80, 40}}; // 3 is verplicht

c[1][2] = 40

Array van standaardstrings

String s[3] = {"aap", "noot"};

s[1] = "noot"

sizeof(a) = 10 * sizeof(int)

Structures

struct structTypeBlaBla

{ int i;

  char c[10]; 

} s, t = {12, "bla"};

initialiseren: s.i = 25;

structTypeBlaBla a, b, c;

struct // zonder het type een naam te geven (niet handig)

{ int i;

  char c[10]; 

} u, v;

Unions

Meerdere variabelen die dezelfde geheugenplaats in beslag nemen, de grootste bepaalt de geheugenruimte.

union {int i; byte j;} u;

Bitvelden / bit fields

Moet in struct

struct bv

{ unsigned blaRight:1, b:1, blaLeft:1;

};

Bitmanipulatie met bitvelden en unions

union

{ bv s;

  int i;

} u;

u.i=0;

u.s.blaLeft = 1;

Resultaat: u.i = 4

Bitmanipulatie

& | ^(xor) ~(not) << >>

int b = ~a

int b = a << 3

Conditionele compilatie

#ifdef x (of #if)

...

#else

...

#endif

variable qualifier

const

·       const int i = 25; // i is niet meer te wijzigen i.p.v. #define

char* const array[] = {"a", "f"}; // Een pointer naar een constant character string

·       fun(const int &x) // x kan niet worden gemodificeerd

·       fun(K &k) const // bij lidfunctie kan er geen data gemodificeerd worden in de de klasse

static

Variable blijft na het verlaten van de functie behouden en wordt niet destroyd.

static int i=0; let op: mag in een lus want wordt maar één keer 0 gemaakt.

volatile

Voor een variabele die kan worden gewijzigd buiten het programma om, bijvoorbeeld in een andere thread of een interrupt routine. Anders kan de compiler de variabele soms weg-optimaliseren.

Cast

static_cast

·       Veranderen van een type, kan op twee manieren:

int v;

(long) v; gebruik liever static_cast<long>

long (v);

reinterpret_cast

Cast voor converteren van pointers die verwijzen naar verschillende objecten, pas op!

const cast

Manipulatie van const pointers.

dynamic_cast

·       Doel: om een pointer naar een basisklasse om te zetten naar een pointer naar een afgeleide klasse. Als de basisklasse geen virtuele lidfuncties bevat voor de lidfuncties van de afgeleide klasse (zoals soms bij Borland VCL) dan moet dynamic_cast worden gebruikt om de lidfuncties van de afgeleide klassen aan te roepen. Bij een goed ontwerp is dynamic_cast niet nodig.

afgeleide_klasse* = dynamic_cast<afgeleide klassetype*>(basisklasse*)

·       Voorbeeld Borland c++:

TMDIChild* ChildPtr=dynamic_cast<TMDIChild*>(MainForm->ActiveMDIChild);

·       Testen op een bepaalde klasse:

if(afgeleide_klasse*)!= NULL // Bij geen resultaat = NULL

·       Dubbele cast

Variant v =(int)(bool)var; // Soms nodig, zie ConfigVariant in dll AVD

Headers

Slechts één keer uitvoeren van een header

#ifndef Headerx

#define Headerx

declaraties ..

#endif

Waarom headers

Verhogen compilatie snelheid. Als alleen de code verandert maar niet de header hoeven de modules die de header aanroepen niet te worden gerecompileerd. Zonder gebruik van headers, dus met .cpp includements moet dit wel.

Tips

·       Geen includes nesten. Headers mogen geen andere include’s bevatten behalve bij overerving en externe hpp files (en zelf gedefinieerde types). Oplossen met forward declaration, zie verder.

·       Gebruik zo weinig mogelijk #include "Main.h" en "ChildWin.h"

pre-compiled header

·       Voor snellere compilatie, zie Borland documentatie voor project options enz.

·       Gebruik een enkele precompiledheaders.h file met de lijst van alle pre-compiled headers:

#include <vcl.h>      // Include common (system) header files

#include “Main.h”    // My project headers

#include <myDLLfiles.h>

#include andere headers

In de cpp file:

#include < precompiledheader.h>

#pragma hdrstop        // Terminate list here

andere headers…

Let op:

·       Alleen in cpp files, werkt niet in headers zelf

·       Er mag geen code in de headers.

·       Er mag geen global variable definition in de headers, dus ook geen static variabelen

Code in header

·       Nadeel: geen precompiled headers mogelijk

·       De code wordt uitgevoerd in de header i.p.v. in de cpp file

·       Bij templates is het handiger omdat anders de .cpp file included moet worden

Forward declaration

Voordelen:

1.      Voorkomt het nesten van includes

2.      Voorkom circular includes door #includes in header te vermijden

Gebruik

1.      Bij declaratie van een klasse of een struct, gebruik pointers naar objecten

In header file:

class K; // Dit is forward declaration. Dus niet #include “K.h”

K *k;

In cpp file:

#include "K.h"

k = new K; // creëer met new

2.      Bij argumenten

In header file:

struct K; // #include “K.h” is niet nodig!

void fun(K k);

vector<K> v;

3.      Forward declaration bij templates:

template <class T>

class xxx;

console applicaties user interface

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

·       cin >> Var1 >> Var2;

·       cout << setw(3)                                                  veldbreedte tot nader order

·       width(3)                                                               zie boven, alleen voor volgende regel

·       cout << setprecision(1)                                    aantal decimalen tot nader order

·       precision(1)                                                        zie boven alleen voor volgende regel

·       cin.clear();                                                           wissen teveel gelezen char

·       cin.get(ch);                                                          lees char.

·       cin.ignore(100,’\n’);                                           lezen ten hoogste 100 char t/m \n        

·       cin >> hex >> i;                                                  hexadecimale invoer

·       cout << hex                                                         hexadecimale uitvoer

·       cout << setfill(‘0’)                                              extra posities 0 i.p.v. spatie

Objecten toevoegen en verwijderen

struct structnaam {int a; int b;};

int lengte = 3;

1.      Niet dynamisch

structnaam vb2[3]; // constante 3 vereist, variabele "lengte" is niet mogelijk

vb2[0].a = 25;

2.      Dynamisch geheugenallocatie met new

structnaam* vb3 = new structnaam[lengte]; // array, lengte is variabele!

vb3[0].a = 25;

delete[] vb3; // let op: gebruik [] bij arrays

3.      Pointers en new

Toepassing array van pointers (naar objecten). Voordeel: het array heeft weinig geheugen nodig terwijl de objecten zelf groot zijn.

structnaam* vb1[3]; // array van pointers, constante 3 vereist

vb1[0] = new structnaam;

vb1[0]->a = 25; // -> nodig want vb1 is een pointer

Met reference (zie references)

structnaam& ref = *vb1[0];

ref.a = 25; // punt . i.p.v. ->

Altijd doen:

·       Pointer declaratie:

int *p = NULL;// Veilig, een pointer mag niet naar een willekeurige geheugenplaats wijzen

·       Pointer verwijderen

delete ptr; ptr = NULL; // Meerdere keren deleten mag bij NULL pointer. Of doe:

if(ptr) delete ptr; // Test op NULL

Bijzonderheden met new en delete

·       Beginadres speciferen

int* p = new(pointerA) int[5] // Beginadres = pointerA. Geheugen wordt zo over elkaar heen gebruikt. Dit kan ook met unions.

·       Wisselbestand

Bij exit komt het geheugen weer vrij. Bij overschrijding RAM wordt een op de harde schijf gebruikt. Zie  programma “new and delete”. Bekijk het geheugen met taakbeheer of met systeembeheer > prestaties / beschikbare geheugen mb.

References

·       Gebruik als pointers maar geen adres van (&) en inhoud van (*) nodig. Zie programma Reference

·       Notatie:

int& myref; //of  int &myref Let op: & betekent niet adres van!

·       Nadeel: initialisatie moet gelijk met declaratie

1.      Simple references

De referentie kan worden doorgegeven

char c = 'a';

char& cr1 = c; // cr1 is een alias (schuilnaam) voor c. Let op: & betekent niet adres van!

char& cr2  = cr1;

cr2 = 'x'; // resultaat: c = 'x' Let op: dit is natuurlijk een linke toepassing

2.      Reference / dereference

Als voorbeeld 1 maar dan met pointers i.p.v. reference. & = adres van. * = inhoud van. int* pointer declaratie

char c = 'a';

char* cp1 = &c; // Let op: hier is & wel adres van!

char* cp2  = cp1;

*cp2 = 'x'; // resultaat: c = 'x'

Voorbeeld in c, argumenten wijzigen kan alleen met adressen

fun(int *x)

{ *x = 25;

}

int i;

fun(&i);

3.      Referentie argumenten

·       Pass by reference:

fun(int& x) of fun(int &x)

(Pass by value: fun(int x))

Toepassingen

1.      Wijzigen van variabelen via parameters:

fun(int &x){x++};

swap(int& Var1, int& Var2);

2.      Doorgeven grote objecten:

fun(const string &x)

Er hoeft geen copie gemaakt te worden en is dus sneller, toepassing bij Variants, Strings enz..

·       Let op: aanroep moet met variable als parameter:

fun(10) kan niet, wel bij fun(const int &x).

·       fun(const int &x) x is niet modificeerbaar door de functie

4.      Referentie als functiewaarde

Toepassingen

·       Wijzigen returnvariabele:

int &kleinste(int &x, int &y)

{ return (x < y ? x : y);

}

int x=1, y=2;

kleinste(x, y) = 0; // x=0 De returnvariabele wordt dus gewijzigd!

·       Setter, getter oplossing / combinatie:

class Reftst

{ public:

  int& access() { return i;} // Getter, setter combinatie

  int i;

};

Reftst reftst;

reftst.access() = 123; // Gebruik als setter. Reftst::i wordt 123

int i = reftst.access(); // Gebruik als getter. i = 123

5.      Reference lidvariabelen

·       Initialisatie via constructor is verplicht bij reference members (lidvariabelen)

int& r; // lidvariabele

Fun(int& i): // Constructor

r(i) //  Verplicht

·       r wijst naar i zolang fun bestaat, zie programma Reference.

6.      Polymorfisme

·       void uitvoer(const K *k) { k->print();} // Verdient de voorkeur

void uitvoer(const K &k) { k.print();} // Reference. Punt i.p.v. ->

// Aanroep:

uitvoer(r)  // bij reference

uitvoer(*p) // bij pointers, nadeel: extra * nodig!

uitvoer(p)  // bij gewoon object

·       Nadeel van reference: Bij dynamic_cast is een pointer vereist

7.      Reference naar inhoud van pointer

·       TRegistry& regKey = *new TRegistry; // of *(new TRegistry)

regKey.OpenKey(); // direct member operator (.) i.p.v indirect member operator (->)

delete &regKey; // let op: &

* nodig bij operators: *var = 500; ipv var = 500;

·       Nadelen:

1.      Reference kan niet NULL worden gemaakt, dus oppassen met 2 x delete

2.      Initialisatie moet gelijk met declaratie

8.      Reference naar pointer

Bijvoorbeeld bij functies die een object crëeeren, de waarde van de pointer verandert hier.

createObject(T* &t) {t = new T);

Templates en Traits

Functietemplate voorbeeld

template <class T>

void incr (T &x) { x++;}

int i = 0; // Voor int

incr(i);

float f=0; // Voor float

incr (f);

Klassetemplate voorbeeld

template <class elem>

class vector

{ elem test(elem x);             

  void ikGeefGeenFoutmelding(){bla bla bla}; // Fout maar geeft geen foutmelding!

};

template <class elem>

elem vector<elem>::test(elem x)

{ return x*x;

}

vector<int> vi; // Type vermelden bij klassetemplates

int x = vi.test(5);

Templates

Toepassing:

1.      Voor het ondersteunen van variabele typen argumenten

2.      Voor het uitvoeren van variabele functies als argument, zie functie objecten

·       Ook wel static polyformisme genoemd. Beter is dynamische polyformisme d.m.v. polymorfe of abstracte klassen.

·       Let op: De code moet ook geïnclude worden, niet alleen de header. Schrijf de code daarom in de header. Gebruik ook het #ifndef en #define mechanisme, ook voor de code. Zie “separate file template compilation”. Zie programma Template.

·       Let op bij templates in DLL: wordt altijd gedebugd ook bij ontbreken van de DLL TDS file.

·       Let op: wordt alleen op gecompileerd en fouten gecontroleerd wanneer de functie wordt aangeroepen, dus altijd alle template functies testen!

·       Bij klassetemplates moet het type worden vermeld b.v. K<int>, bij functietemplates niet.

·       Om bij klassen het type niet te hoeven vermelden gebruik de templates alleen bij de lidfuncties

·       Bij Linker Error Unresolved external .. xxx.obj. Plaats in xxx.cpp de .cpp file met de template lidfuncties #include .cpp

·       Afgeleide klasse van een template klasse: class TAfgeleide: public TBasis<int>

·       Overerven van templates: afgeleide klasse van een template klasse met 2 templates, als 2e template wordt weer een template: class TAfgeleide: public TBasis <int, class float>

Traits

·       Uibreiding op templates, zie Borland Standard c++ library > Users Guide > Part V: Special techniques

Special classes

·       Zie Standard Borland Standard c++ library > Users Guide > Part VI: Special classes

auto_ptr

·       Voordeel: wordt altijd gedelete in destructor, voorkomt memory leaks. Strikt ownership: een object wordt altijd door maximaal één auto_ptr aangewezen, bij copieëeren wordt één pointer NULL.

·       Nadelen: o.a. werkt niet goed bij containers en arrays. Debuggen is lastig want de inspector geeft de auto_ptr aan i.p.v. het gewenste object.

complex

·       Voor werken met complexe getallen

numeric_limits

·       Speciale klasse om limits te bepalen

Voorbeeld:

#include <limits>

using namespace std;

numeric_limits<double> doubleInfo;

int dig = doubleInfo.digits10; // Equals 15

double e = doubleInfo.epsilon; // Equals 2.22044604925031E-16

Functies

Begrippen

·       Parameter: i in fun(int i){}

·       Argument: 123 in a = fun(123);

·       Call by reference fun(int& x) of fun(int &x), zie references

Default  parameters

Moet in header.

TestFunc(int i = 5)

i kan weggelaten worden want default = 5, let op: bij meerdere parameters default  parameters toevoegen vanaf het einde van de parameter list.

Function overloading

·       Functies (en lidfuncties) met dezelfde naam maar met verschillende parameters

fun(int i){};

fun(char c){};

·       Tip: gebruik functie overloading als default parameters niet mogelijk zijn

·       Gebruik van const klasse object

Een functie met const en een functie zonder const:

int func(int i) const // Wordt aangeroepen met k

int func(int i) // Wordt niet aangeroepen met k

Const klasse object:

const K k; 

k.func(123); // Roep de 1e functie aan

Functie pointers

·       void fun(int i) {};

void (*p)(int); // p is functiepointer naar alle functies zoals void functienaam(int);

p = fun;

p(123); // Roep de functie fun aan

·       In c++ kan dit beter met het Strategy pattern

·       Bij klassen alleen voor static lidfuncties.

·       De functiepointer kan alleen wijzen naar functies met identieke argumenten

Static function pointer

With typedef:

typedef void (*switchCallback_t)(void*); // typedef in header
static switchCallback_t _beepAllCallback = nullptr; // declaration and initialisation in header
switchCallback_t Switch::_beepAllCallback; // definition outside class is mandatory

Without typedef:

void (*Switch::_beepAllCallback)(void*); // definition outside class is mandatory
static void(*_beepAllCallback)(void*) = nullptr; // declaration and initialisation in header

Inline

·       Een lidfunctie gedefinieerd in de Klasse is inline by default

Variadic function

Een functie met een variabel aantal parameters, doen mbv va_list en …

Operator overloading

·       Toepassing technische functies, maak eigen operatoren

·       Bij pointers kan niet -> gebruikt worden, b.v. p->+ bij + operator. Oplossing: (*p)+

·       Ook mogelijk: operator new en operator delete

Dyadische operator (2 operanden)

Er zijn 2 mogelijkheden:

1.      Lidfunctie met operator, assymmetrisch:

breuk breuk::operator+(const breuk &a){ }

ostream &operator<<(ostream &os, const lang &u){}

2.      Gewone functie (of friend functie) met operator, symmetrisch:

breuk operator/(const breuk &a, const breuk &b){ }

Dit heeft de voorkeur. Er kan voor beide argumenten gebruik worden gemaakt van impliciete conversie, zie “impliciete conversie”. Bij de assymmetrische vorm moet één argument altijd een klasse object zijn.

monadische operator (1 operand)

·       Prefix operator

K operator ++() {} // Prefix. Geen dummy argument nodig

·       Postfix operator:

K operator ++(int) {} // int is een dummy argument, nodig voor de Postfix operator

Conversion operator

·       Converteert een object naar een (ander) type

Toepassingen:

1.      Getter zonder gebruik van een functienaam

Normale getter functie:

double T::get(){return v;} // Lidfunctie

T t;

Aanroep:

double y = t.get(); // Met functienaam

Getter met conversion operator:

T::operator double () {return v;} // Let op notatie

Aanroep:

double y = t; // Object wordt geconverteerd naar double, geen functienaam nodig

2.      Converteren naar een ander type

Als boven maar dan voor int:

T::operator int () {return v;} // Conversie naar int

Klassen

Begrippen

·       Ancester = klasse voorouder

·       Parent = ouder

·       Descandant = afstammeling

·       Derive = afgeleide

·       Access specifiers: private, public

·       Scope override ::

·       Klasse object (of instantie van een klasse) is een variable van het type van de klasse.

·       Klasseleden = functieleden en dataleden

·       Functieleden = lidfuncties

·       Dataleden = lidvariabelen

·       Instantie = object

·       Specialisatie: een subclass is een specialisatie van een superclass.

·       Generalisatie: een superclass is een generalisatie van de subclasses

Algemeen

·       Het voordeel van klassen is de sterke koppeling van data en functies

·       Korte en pure virtuele functies kunnen in de header:

int GetXMin(){return XMin;}

Nadeel: bij code in de headers is geen pre-compiled header mogelijk

·       This

Is de pointer naar het actuele object.

this->lidvariabele is gelijk aan lidvariabele

·       Bij meerdere files per klasse neem de filenaam: ClassName_section1.cpp

·       Standaard klasse-interfaces:

1.      constructor, initialiseer hier de variabelen

2.      destructor, verwijder hier de variabelen weer (bij gebruik van new)

3.      copy functie: copy constructor (niet altijd nodig)

4.      copy functie: operator = functie (niet altijd nodig)

Structures

·       Gebruik als klasse maar alleen public mogelijk en geen private en protected

Relaties

·       Client class gebruikt diensten van een andere class

·       Server class verleent diensten aan andere class

·       Agent is een client en een server tegelijk

·       Klasse interface = de public methods die clients kunnen gebruiken

·       Contract is een gebruiksaanwijzing tussen de client en de server (server interface)

·       Contractbreuk (bij een fout) geeft een exception door de server

·       Aggregaat relatie: een klasse bevat weer een andere klasse

Soorten klassen

Zie ook Design Patterns

1.      Abstracte klasse

Een klasse die een of meer pure virtuele lidfuncties bevat:

K::virtual fun()=0;

Hiervan zijn geen objecten te creëren (wel pointers). fun() moet in een afgeleide klasse worden gedefieerd

2.      Concrete klasse

Een niet abstracte klasse.

3.      Polymorfe klasse

Een klasse die virtuele lidfuncties bevat, zie “polymorfisme”:

K::virtual fun();

4.      Lege klasse

class noemerNul {}; // lege klasse

throw noemerNul(); // noemerNul is een anonym object van het type noemerNul

5.      Anoniem object

Dit is een object zonder objectnaam.

Voorbeeld:

K(); // i.p.v. K k().

K().fun(); // Aanroepen lidfunctie van een anoniem object.

int returnvalue = K().fun(); // ook returnvalue mogelijk

Toepassingen:

·       Functie objecten

·       TestAll() // Run de constructor met testfuncties zonder een apart object te gebruiken

Let op: de destructor wordt aangeroepen na de constructor

Let op: bij een constructor met één argument dat een pointer is en waarbij gebruik wordt gemaakt van een anonym object gaat het fout:

K::K(int* i){} // Constructor

Anonym object creëren:

int* i;

K(i); // Compiler error: Multiple declaration for i;

Oplossing:

int* i;

K((int*)i); // Cast verplicht

Ander voorbeeld:

int i = int(); // i = 0

Werken met klassen

Classe voorbeeld

Header:

class K: public Base

{

public:

  K(int x); // Constructor

  ~K(); // Destructor

  int fun(); // Lidfunctie

protected:

private:

  int i;

};

cpp file:

K::K(int x):

i(0) // Initialiser

{ i=x;

}

int K::fun()

{ return i;

}

Noodzaak van pointers naar objecten

1.      Vermijdt het nesten van includes door toepassing van forward declaration

2.     Bij het opslaan van objecten in containers is het opslaan van pointers naar objecten efficiënter dan het opslaan van het object zelf omdat er minder geheugen nodig is.

Constructors

·       Kunnen niet virtueel of private zijn, alleen public.

·       Function overloading is ook mogelijk bij constructors

Constructors zonder argument

·       Geen haakjes gebruiken bij declaratie: K k; fout: K k(); want dit is een functie declaratie

·       Declaratie in een header kan alleen met constructors zonder parameters. K k; Dus niet: K k(5); Hier moeten we dynamisch creëren.

Constructors met één argument

·       Zie ook implicite conversie

·       Bij aanroepen constructor met = voor een bestaand object b.v. k=5; wordt de constructor opnieuw aangeroepen!

·       Let op: bij een constructor met één argument dat een pointer is en waarbij gebruik wordt gemaakt van een anonym object gaat het fout. Zie voorbeeld anoniem object.

Destructors

·       Delete objecten in de destructor.

·       Pure virtual destructors zijn niet toegestaan, gebruik een lege body ~fun(){};

·       Destructors kunnen apart worden aangeroepen met ~fun();

·       Maak voor de veiligheid álle destructors virtueel omdat het vergeten hiervan niet opgemerkt wordt en geheugenlek veroorzaakt doordat delete instructies in de destructor niet worden uitgevoerd. Kan niet in Arduino.

Impliciete conversie

·       Kan alleen bij constructors met één argument

·       Expliciete conversie K k(5)

·       Impliciete conversie K k=5

·       Toepassing:

fun(K k);

fun(1234); // aanroep

De constructor van K wordt aangeroepen met 1234 als argument. Dit is implicite conversie

·       Implicite conversie voorkomen met explicit.

Lidvariabelen

·       Problemen bij identieke functieparameternaam en lidvariabelenaam “x”:

1.      Constructor

Class(int x):

x(x) { } // Initialisatie van lidvariabele

2.      Doorgeven van constantes

const int x;

Dit kan alleen zo:

Class(int x):

x(x) { }

3.      Lidfunctie

fun(int x)

{ this->x = x;} // Moet met this, maar dit is onhandig, gebruik fun(int _x)

·       Reference lidvariabelen int& r; zie references

Lidfuncties

·       Soorten lidfuncties: constructors, destructors, selectors (er verandert niets) , modifiers

·       lidFunctie() const;// De functie kan de lidvariabelen niet wijzigen. Niet noodzakelijk maar wel aan te bevelen.

·       Een lidfunctie gedefinieerd in de Klasse is inline by default

·       Lidfunctie aanroep bij object creatie

Singleton* s = new Singleton->Instance(); // Returnvalue moet pointer zijn.

Static lidfunctie

·       Static lidfunctie is buiten de klasse te gebruiken zonder object X::fun();

·       Heeft alleen toegang tot static lidvariabelen

·       Kunnen niet overgeërfd worden

Static lidvariabelen

·       Behoort tot de klasse en niet tot de instanties. Is voor alle objecten gelijk.

·       Verplichte definitie buiten de klasse

class K

{ static int i; };

int K::i; of int K::i = 25; // niet in de header

·       Bij gebruik van templates zijn meerdere regels nodig: int K<double>::i = 25; K<float>::i = 25; enz..

·       Initialiseren van static lidvariabelen kan niet in de initializers van de constructor

Lidfunctie pointer

·       Zonder closure pointer

Bij gebruik van een lidfunctiepointer moeten de lidfuncties static zijn want de functiepointer heeft alleen een functieadres en geen apart objectadres. Static lidfunctie zijn voor alle objecten identiek dus is geen verwijzing naar een object nodig. void (*ptr)(int);

ptr = K::staticMemberFunc; // K is een Klasse, geen object

ptr(2000);

·       Met closure pointer

Closure pointer is geen c++ maar een Borland c++ keyword extension. Een closure maakt een functiepointer naar een niet static lidfunctie mogelijk. Een closure pointer heeft een adres naar een object én een adres naar een functie.

voorbeeld:

void (__closure* closurePtr)(int);

closurePtr = k1.nonStaticMemberFunc;

closurePtr(9999);

Friends

·       Een friend is een gewone (globale) functie of klasse. Beter vermijden. Het enige voordeel is dat een friend gedeclareerd kan worden in een klasseheader.

1.      Friend functie

·       friend void fun();

·       Toepassing: maak dezelfde syntax als ingebouwde typen: cos(x) i.p.v. object.cos()

2.      Friend class

·       Beter vermijden, friend class K;

3.      Friend lidfunctie

·       Beter vermijden, friend void K::fun();

4.      Friend operatoren

·       Bij eigen operatoren verdienen gewone functies de voorkeur boven lidfuncties. Declaratie in de klasse met friend: friend K operator+(const K& x, const K& y) // Gewone functie in klasseheader

Virtuele lidfuncties

·       Basisklasse:

virtual fun() {}; // leeg

virtual fun() {code};

Toepassing 1: gebruik de lidfunctie uit de basisklasse of de lidfunctie van de afgeleide klasse als deze bestaat. Lege virtuele lidfuncties gebruiken als er geen code nodig is.

Toepassing 2: lidfuncties van afgeleide klassen aanroepen vanuit de basisklasse.

·       virtual fun() = 0;

Pure virtuele functie. Om een abstracte basisklasse te definieren. Lidfunctie in afgeleide klasse vereist.

Functie objecten

·       Een functie-object is een klasse met een operator () lidfunctie.

·       Doel is functies doorgeven (Strategy pattern) aan een functie “voeruit”:

       template <class Functie>

       void voeruit(Functie f)

       { f(1234);                                 

       }

Deze functie accepteert gewone functies én functieobjecten (dit is een klasse!)

·       Functieobject

Het voordeel van een functie-object is dat een (extra) parameter via de constructor kan worden doorgegeven. Er wordt een lidfunctie van het object doorgegeven. Dit geeft meer mogelijkheden dan bij het doorgeven van gewone functies:

                 class K

       void operator()(int x) // Lidfunctie met operator ()

       {}

Werking: Omdat er een operator () lidfunctie wordt gebruikt is geen functienaam nodig en is de interface voor alle functies gelijk, n.l. ().

voeruit(K());

Er wordt een anoniem object K() als argument doorgegeven aan voeruit, de operatorfunctie () wordt uitgevoerd: f();

Met getal 100 doorgeven aan constructor van K: voeruit(K(100));

·       Gewone functies

Kunnen ook worden doorgegeven als argument i.p.v. functie-objecten omdat gebruik wordt gemaakt van een template

       void GewoneFun(int x)

       {}

       voeruit(gewonefunctie);

·       unary_function: Een basis klasse voor unary function objects in Borland c++.

Overerving

Tips

·       Vermijd diepe overervingshiërarchieën, dit is moeilijk te begrijpen

·       Meervoudige overerving is in 95% van de gevallen onnodig en geeft botsingen tussen gelijknamige lidfuncties

Public, private of protected overerving

·       Private klasseleden zijn alleen zichtbaar binnen de klasse

·       Protected zijn ook zichtbaar in afgeleide klassen

·       Public klasseleden zijn altijd zichtbaar

·       class K: public B

Public klasseleden van B zijn public in K

·       class K: protected B

Public klasseleden van B zijn protected in K

·       class K: private B

Public klasseleden van B zijn private in K

Constructors en destructors

·       Basisklasse constructor wordt eerst aangeroepen, daarna die van de afgeleide klasse.

·       Met destructors is het omgekeerd.

·       Doorgeven waarde aan constructor basisklasse B

cpp file:

K::K(int x):

i(0), B(x)

Scoperesolutie parameter

·       Toepassing 1. Aanroep forceren van een andere lidfunctie i.p.v. de lidfunctie die normaal zou worden aangeroepen zoals de lidfunctie van de afgeleide klasse.

B::f() //roep altijd lidfunctie f aan van de klasse B

·       Toepassing 2. Public maken van een (public) functie B::fb (of variabele) van een basisklasse B, die anders private zou zijn:

ClassD : private B

{ public:

  B::fb; // fb was private lidfunctie van B, maar is nu public!

}

Polymorfisme

·       Een klasse is polymorf als hij minimaal één virtuele (of pure virtuele) lidfunctie heeft (of erft)

class B

{

public:

  virtual int& value()=0; // pure virtuele lidfuncties

};

class A: public B

{

public:

  int& value() // reference als functiewaaarde (setter / getter combinatie)

  { return i;

  }

private:

  int i;

};

  B* b = new A; // b is een pointer die geschikt is om naar andersoortige (afgeleide) objecten te wijzen

  b->value()=1234; // Setter / getter combinatie

  int i = b->value();

Copiëren van klasse objecten

·       Bij echt Kopieëren van klassen is een diepe copie gewenst. Kopiëren kan op 2 manieren:

K b(a); // copy constructor nodig

b=a; // assignment operator = nodig

·       Voorbeeld

class K

{

public:

  K(int i):

  i(i){}; // constructor

  K(const K& k) // copy constructor, moet met & volgens c++

  { i = k.i;

  }

  K &operator=(const K& k) copy assigenment operator

  { if(this != &k) // Bij k1=k1 mag er vaak niets gebeuren zoals deleten van pointers

    { i = k.i;

      return *this; // i.v.m. kopiëren k1=k2=k3

    }

  }

private:

  int i;

};

  K k1(1234), k3(1);

  K k2(k1); // gebruik de copy constructor

  k1=k2=k3; // gebruik de assigenment operator

·       Copy constructor bij overerving

Kafg:: Kafg (const Kafg & k) : KBasis(k) // Roep de KBasis copy constructor aan

{ code }

·       De toekenningsoperator wordt niet geërft

Roep in de toekenningsoperatorfunctie de toekenningsoperator uit de basisklasse aan met KBasis::operator = (k);

Strings

1.      Null-terminated strings

Let op: dit zijn arrays of adressen

·       Ook wel genoemd c stijl string

·       char* str = "tekst"; // "tekst" levert een adres af, \0 wordt toegevoegd

·       *"tekst" = t / "tekst"[0] = t

·       char str[10] = "tekst"; // string variable (array) met vaste lengte *str = str[0] = t

·       Routines in Borland c++ :

1.      Visual Component Library Ref. > Categorical Routines Listing > String handling routines (null-terminated)

Deze beginnen met hoofdletters: AnsiStrComp, StrCat

2.      C runtime library reference > Categerical routines and Types Listing >

c++ Prototyped Routines strstr

Classification Routines isalnum

Conversion Routines itoa

Input / output Routines scanf

Manipulation Routines strcat

2.      Standaardtype string (basic_string)

·       Let op: functies werken ook voor c stijl strings

·       string str;#include <string>, using namespace std;

·       Standard C++ Library > Users guide > Part III: containers > string

·       Gebruik liever String

3.      AnsiStrings

·       String = AnsiString (dit gaat automatisch)

·       Gebruik altijd String voor strings, variabele lengte enz.

·       Let op: er zijn veel meer functies dan Ansistring methods! Zoek in help op “String handling routines”

·       Routines in Borland c++ :

Visual Component Library Ref. > Categorical Routines Listing > String handling routines

int AnsiCompareStr(const AnsiString S1, const AnsiString S2);

·       AnsiCompareText, AnsiSameText, CompareText: without case sensitivity.

·       “Menu support routines”

AnsiSameCaption, ShortCutToText enz.

·       Omzetten van string naar getalwaarden StrToInt

·       Voorbeelden:

String s = 5; // s = “5” Conversie integer en floats enz. naar String

String v=“Deze tekst mag niet worden”    // Splitsen van tekst

“gesplitst.”;;                  

s=v[2] // s = ”b”

Mystring.c_str(); //Conversie AnsiString naar c string (char array)

StrToInt(Edit3->Text); // Conversie string naar int, nadeel throws een EConvertError exception

String S2 = S1.SubString(1,pos);

String Y = “XX” + S2; // Let op: één van beide moet van het type String zijn

Containers

Soorten containers

Containers selecteren

·       Zie Borland c++ : Standard c++ library > Part III: containers > Container classes > Selecting a container

·       Let op snelheid, R/W bij vector het snelst

Sequence containers

De volgorde blijft behouden

Map                           Random access x[key], 2 Dimensionaal, altijd gesorteerd, unieke elementen. Read, write: 220 nsec

Multimap                                   Random access x[key], 2 Dimensionaal, altijd gesorteerd, niet unieke elementen,

Vector                       Random access x[i], efficiënt insert/delete op eind. Toevoegen van data (push_back) 120 nsec.

R/W 12 ns. Toepassen bij als de lengte niet van te voren bekend is en vaak wordt gewijzigd.

Deque                      Random access x[i], als vector, efficiënt insert/delete op begin of eind

List                            Snel insert, alleen sequentieel access

Eenvoudig elementen verwijderen: remove(const T& element); // verwijdert alle elementen gelijk aan element, hoeft niet in de list voor te komen

erase(iterator position)

return 1e element chartDatas.front();

Stack                         LIFO

Queues                    FIFO

priority_queue        FIFO, als Queues

Gesorteerde associatieve containers

Gesorteerd opslaan, de volgorde blijft niet behouden

Set                             Verzameling unieke elementen.

Eenvoudig element verwijderen: erase(iterator position) of erase(const key_type& x)

Multiset                     Als set, niet unieke elementen

Pair                           Ook te gebruiken voor het retourneren van meer dan 1 variabele door een functie

DynamicArray         Dynamisch van grootte veranderen. Resize is traag >4us. Toepassen als de lengte niet van tevoren

bekend is en niet vaak wordt gewijzigd. R/W 17 ns, trager dan vector!

DynamicArray<int> a;

a.Lenght=10;

a.Lenght=11;   // resize

a.high                     // = Length-1.

Valarray                   Voor efficiënte numeric programming

Container functies

Voor alle containers

begin()                     Returns an iterator or a const_iterator pointing to the first element in the collection

end()                         Returns an iterator or a const_iterator pointing just beyond the last element in the collection

swap(                       Swaps elements between this container and the swap’s argument

clear()                       Deletes all the elements in the container

size()                        Returns the number of elements in the collection as a size_type

max_size()              Returns the largest possible number of elements for this type of container as a size_type

empty()                    Returns true if the container is empty, false otherwise

get_allocator()        Returns the allocator used by this container

Container algoritmes

Er is een hele verzameling van algoritmes voor containers, zie Standard C++ Library: User’s Guide / Agorithms

for_each

·       Voert een functie f uit (met de iterator als argument) voor de elementen.

·       Als f een waarde aflevert wordt deze genegeerd.

·       void for_each(InputIterator first, InputIterator last, Function f);

·       Aanroep: for_each(v.begin(), v.end(), f);

·       Let op: Functie f of functie-object f moet een functieparameter hebben van het type van de container:

·       vector<int> Functie voorbeeld f(int x)

·       map<String, int> Functie voorbeeld f(pair<int, int> Pair) of met value_type.

·       Bij een lidfunctie moet f static zijn.

·       Een functieobject fo is nodig voor het eenmalig doorgeven van een variabele (5).

·       Aanroep: for_each(v.begin(), v.end(), fo(5)); // Geef 5 door aan de constructor

find(k)

Returns an iterator pointing to an element with key equal to k or end(), if such an element is not found

count(k)

Returns the number of elements with key equal to k

find_if

·       De (test) functie moet een bool variabele (true / false) afleveren.

value_type

·       The type held by the container, a key/value pair. (typedef pair<const Key, T> value_type;)

·       typedef map<string, int> MyMapType;

·       typedef MyMapType::value_type My_PairType;

·       My_PairType MyEntry

·       cout << MyEntry.first << MyEntry.second << endl;

Alle elementen doorlopen bij map

typedef map<String, ConfigVariant*> mapType;

mapType myMap;

mapType::iterator i;

for(i = myMap.begin(); i != myMap.end(); i++) delete (*i).second; // b.v. delete

Verwijderen van een element in een vector

chartDatas.erase(find(chartDatas.begin(), chartDatas.end(), element));

Testen of de container een element bevat

if(find(chartDatas.begin(), chartDatas.end(), element) == chartDatas.end()) return;

Tips

Aanbevelingen

·       Gebruik forward declarations, altijd pointers naar objecten (kan niet bij microcontrollers)

·       Variabelen altijd initialiseren, b.v. in de constructor

·       Gebruik altijd zo veel mogelijk const als het kan

·       Vermijd globale variabelen en anders houd globale variabelen en functies lokaal binnen de file (of beter binnen de functie) met static

·       Bij switch gebruik geen overbodige default case

·       Gebruik safeDelete() {delete ptr; ptr = NULL;}

·       Maak voor de veiligheid alle destructors virtueel

·       Gebruik referentie argumenten bij doorgeven van grote objecten. fun(const string &x). Er hoeft geen copie gemaakt te worden

·       Afronden geeft soms problemen met floats. Oplossing: Decimal class, heeft grondtal 10.

·       Controleer af en toe de folders van de files in Borland c++ > View > project manager:!

Opmaak

Zie ook Google C++ Style Guide

Klasse header:

public:

methods

data members

private:

methods

data members

·       else if op dezelfde regel

·       While op dezelfde regel als de closing brace: } while( x < y );

Let op

·       Use delete[] at arrays.

·       B is executed twice in max(A, time-consuming operation B).

·       Don't call virtual functions in a constructor or destructor.

·       Don't store anonymous objects into a container.

·       Use {} at nested if / else statements.

·       Delete ook alle objecten waarvan de objectpointers in een container zijn opgeslagen bij het deleten van de container

  while(!cont.empty())

  { safeDelete(cont.back());

    cont.pop_back();

  }

·       Void vóór functies niet vergeten

·       Kijk uit bij scanf, %hu gebruikten voor type Word

·       Gebruik niet variabelenamen Function, Update(), delete enz.

Let op bij Borland c++

·       Aansturen VCL componenten vanuit lidfunctie: met ActiveMDIChild en dynamic_cast.

·       Keyword __published niet aankomen.

·       In console applicatie: toevoegen #include <conio.h> en getch() op het eind;

·       #include <vector> is niet altijd #include <vector.h>

Lastige compiler errors

·       In header foutieve naam achter #ifndef. Oorzaak is fout bij kopiëren

·       Bij een constructor met één argument dat een pointer is en waarbij gebruik wordt gemaakt van een anonym object gaat het fout. Zie voorbeeld anoniem object.

·       Voorkomen warning “parameter … is never used”: vermeld het type zonder de variabele: fun(int, int, double d)

Lastige linker foutmeldingen

·       Sluit klasse declaratie af met ;

·       Fun(int x)

{ x = x; // gaat fout, doe x = _x of this.x = x;

}

·       Bij static datamembers verplichte definitie buiten de klasse

·       Gebruik forward declaration bij circular includement in header

·       “Unresolved external .. xxx.obj” Plaats in de cpp file de template lidfuncties #include .cpp

Lastige runtime foutmeldingen

·       Let op overschrijding van arrays char str[100];

·       Fouten bij geheugen toewijzing

fun(char* c)

{ sprintf(c, “bla”); // c wordt gevuld

}

Aanroep:

char* ch; fun(ch); // Gaat fout want ch heeft nog geen geheugen toegewezen gekregen

·       Scope van een variabele is verlopen:

fun(char* &ref)

{ char str[6] = "E-21";

  ref = str; // Tot hier gaat het goed

}

Aanroep:

char* arg;

fun(arg); // Fout: na beïndigen van fun wijst arg naar een stuk vrijgegeven geheugen

Voorbeeld2:

fun(char* prefix)

{ char* p = “bla”;

  prefix = p;

}

Aanroep:

char* ch;

fun(ch); // Fout: de geugeninhoud van prefix wordt vrijgegeven

·       char* str = "E-21"; // Pointer naar een vaste geheugenplaats, is niet hetzelfde als:

char str[6] = "E-21"; // Array met initialisatie

·       Form ontbreekt in: project options > forms > auto create forms

·       Aanroepen van een lidfunctie van een object dat nog niet gecreëerd is.

·       In de constructor van een basis klasse wordt een lidfunctie van een afgeleide klasse aanroepen. Foutmelding “Pure virtual function called”. De afgeleide klasse is nog niet volledig gecreëerd. Oplossing: maak een functie init() in de basisklasse die de functies van de afgeleide klasse oproept. Roep init() aan in de constructor van de afgeleide klasse.