Det finns tre relationer i OOP. HAR relationen, KÄNNER TILL relationen och ÄR relationen. ÄR relationen beskrivs med ARV.Man åstadkommer arv genom subklasser
till klasser man radan har.Man skapar klasser som delvis är lika men ändå olika och det beskrivs med POLYFORMA klasser. för objekt som tillhör polyforma klasser kan det finnas operationer med samma namn, men som utför olika saker beroende på objektet.
Dynamisk bindning eller sen bindning – bindningen sker inte förrän man kör programmet.
Statisk bindning eller tidig bindning – bindningen ske när man kompilerar koden.
Konstruktoer destruktorer vänfunktioner och tilldelningsoperatorn ärvs inte. Man kan
ärva i flera led. Basklass till subklass till ytterligare subklasser:
class D:
tillgänglighet (public, protected private) B {
…….
};
Klassen D är direct härledd (derived) från
klassen B. Klassen B är en direkt basklass till D
D ärver alla
medlemmar från B.
Undantag. Konstruktorer, destruktorer,
vänfunktioner och tilldelningsoperatorn =
Medlemmar med samma
namn gömd.
class D2 :
tillgänglighet D{
….
};
Klassen D2 är
indirekt härledd från klassen B. Klassen B är en indirekt basklass till D2
D2 ärver alla medlemmar
från B och D.
Objekt som tillhör en
härledd klass initieras som andra objekt med hjälp av konstruktorn.
När en sådan konstruktor anropas måste även de
datamedlemmar som ÄRVTS från basklassen initieras. Detta sker genom att konstruktorn för basklassen anropas.
Konstruktorn för basklassen anropas alltid inna datamedlemmarna i den härledda klassen initieras.
Före en härledd klass D med den direkta basklassen B gäller:
I en konstruktor för D kan man i initieringslistan explicit anropa en konstruktor för basklassen:
D::D(parametrar) :
B(parametrar), …. {satser}
Har detta ej gjorts, anropas aoutomatiskt B:s default konstruktor.
Om klassen D har en aoutomatiskt genererad default konstruktor så anropas i denna basklassens defalt konstruktor.
Initieringsordning i en konstruktor:
Destruktorerna ärvs inte. En destruktor används i normala fall bara när man allokerat minne som behöver frigöras. Allting sker i motsatt ordning mot konstruktorerna. Det innebär att destruktorn för den mest härledda klassen exekveras först.
private kan bara användas av medlemsfunktioner av klassen och friend funktioner
protected medlemsfunktioner, friendfunktioner och härledda funktioner
public kan användas överallt
class D : private B
{ ……..}
class D :
protected B { …….. }
class D : public B
{ ..…… }
·
privata
medlemmar i B blir aldrig synliga någon annanstans än i B oberoende av arv
·
privat
arv (private:) alla medlemmar i B som är skyddade eller synliga blir privata i
D
·
skyddat
arv (protected:) alla medlemmar i B som är skyddade
eller synliga blir skyddade i D.
·
synligt arv
(public:) alla medlemmar i B som är synliga blir synliga i D. Synligt arv är
det vanligaste (public).
Vid typade programspråk, alltså där man kan ha olika funktioner beroende på vilka typer parametrarna har (överlagring), bestäms anropen till funktionerna vid kompileringen. Man säger att anropet binds till funktionen. Via mallar kan man också göra generiska funktioner
s.k. templates, där elementen inte behöver vara av fördefinierade typer, int, double osv utan
av godtycklig typ. Överlagring och generiska programenheter är två former som i programmeringssammanhang kallas för polyformism (mångformighet). Både när det gäller överlagring och generisk programmering sker bindning vid kompilering av programmet. Detta kallas för statisk bindning eller tidig bindning. Man kan också ha dynamisk bindning eller sen bindning och då sker inte bindningen förrän vid exekveringen av programmet. För att detta skall vara möjligt använder man sig av arv och virtuella funktioner. Via pekare får man sedan den sena bindningen vid exekverandet av programmet. En klass som har virtuella funktioner kallas för polyform klass.
Begrepp:
OBS för att få
virtuella funktioner måste man ha samma parametrar och returtyp
på funktionerna. Gör man inte det döljs den virtuella funktionen i bas klassen.
Den aktuella funktionen måste deklareras som VIRTUELL i basklassen.
virtual returtyp f(parametrar);
Omdeklareras i subklasserna med exakt samma parametrar och retur typ. (Ett speciellt undantag finns beträffande returtypen) fungera ej på min kompilator
Ordet virtual behöver ej upprepas.
Statisk typ – Den typ ett uttryck har enligt deklarationen
Dynamisk typ – Den typ ett uttryck har vid exekveringen
anrop av virtuell funktion:
p->f(..); //p är en pekare
r.f(…), //r är en referens
Vilken funktion som anropas bestäms av p:s och r:s dynamiska typ
/* fungerar inte i VisualC++6
if(typeid(*fp) == typeid(Personbil))
cout << "Detta är en perosnbil" << endl;
else if(typeid(*fp) == typeid(Buss))
cout << "Detta är en buss" << endl;
else if(typeid(*fp) == typeid(Minibuss))
cout << "Detta är en minibuss" << endl;
else if(typeid(*fp) == typeid(Lastbil))
cout << "Detta är en lastbil" << endl;
*/
cout << typeid(
fp).name() << endl;
//ger utskrift: class Fordon*
Filen typeinfo inkluderas
typeid(*p) == typeid(C) //p = pekare
eller
typeid( r) == typeid(C) //r = referens
typeid(*p).name() //ger en textsträng med namnet på klassen
eller
typeid( r).name() // ger en textsträng med namnet på klassen
dynamic_cast<C*>(p) //p omvandlas till typ pekare till C, annars 0
dynamic_cast<C&>(r) //r omvandlas till typ referens till C, annars genereras bad_cast
Deklarera alltid en virtuell destruktor om klassen skall vara en basklass för andra klasser.
En klass som innehåller minst en ren virtuell funktion kallas för abstrakt klass
förväxla den inte med abstrakta datatyper = typer som man definierar själv.
En virtuell funktion (pure virtual function) saknar implementering.
Ex virtual void ge_info() = 0; //nollan markerar att det är en virtuell funktion
En abstrakt klass blir ett mönster för de primitiva funktioner som skall finnas och det är inte meningen att man skall göra ett objekt av den.
En medlemsfunktion kan deklareras som ren virtuell funktion
virtual resultattyp f(parametrar) = 0;
En klass kan ha flera direkta basklasser
class D : tillgänglighet B1, tillgänglighet B2 ….{
deklarationer av egna medlemmar
};
Varje bas klass ingår i sin helhet som ett subobjekt. Alla medlemmar från alla basklasser ärvs.
Namnkonflikter kan uppstå men löses med operatorn ::
en virtuell basklass v ingår som subobjekt exakt en gång i ett objekt.
class C : tillgänglighet virtual V {
deklarationer av egna medlemmar
};
När man programmerar använder man assert från include filen <cassert> för att testa villkoret. Är det inte sant avbryts programmet. Det finns inga möjligheter att fortsätta med programkörningen.
Klassen exeption har rutiner för felrutiner. I filen stdexcept finns andra rutiner och exception
också, så det räcker att ladda in stdexcept.
exception
logic error
runtime_error
domain_error
invalid_argument
range_error
length_error
overflow_error
out_of_range
där uttryck kan vara av godtycklig typ. Den normala exekveringen avbryts när en throw sats exekveras. Bäst är att använda en standard klass från exception throw s(”text”) eller någon
egen klass härledd från klassen exeception. du måste inkludera filen <stexcept>
Att fånga exception:
try {
satser
throw overflow_error(”Fel i x”);
}
catch (parameter) {
satser
}
catch (parameter){
satser
}
catch (…){ //tar alla fel
satser
}
I en funktionsdeklaration kan man lägga till en händelselista sits:
retur typ f(parametrar) throw(T1, T2, T3 .. );
throw(overflow_error, out_of_range,
bad_exception)
Funktionen kan då bara generera exceptionella händelser av de typer som anges I listan.
En tom händelselista innebär att inga exceptionella händelser kan genereras av funktionen.
returtyp f(parametrar) throw();
Saknas händelselista kan händelser av alla typer genereras:
returtyp f(parametrar);
Om det genereras en exceptionell händelse av en typ som inte är angiven på händelselistan, anropas funktionen unexpected, som avbryter programmet.
unexpected kan ersättas med egen funktion:
set_unexpected(namn_på_egen_funktion);
Denna får generera händelse av typen bad_exception.
void my_unexpected()
{throw bad_exception;}
set_unexpected(my_unexpected);
Klasser som hanterar datasamlingar kallas för containerklasser.
Finns två huvudgrupper:
SEKVENSER och ASSOCIATIVA CONTAINERS
SEKVENSER:
till sekvenser räknas också ADAPTERKLASSERNA
Det finns också en samling färdiga funktioner som man kan använda för att hantera containerklasserna (ALGORITMER)
Vector och deque
kan behandlas som vanliga fält. Man har möjligheten att indexera för att löpa
igenom dem effektivt, men man kan inte använda pekare på dem. Det finns i
stället iteratorer. En iterator är en fristående pekarlik variabel som man
kopplar till en viss datasamling tex
vector eller lista. jIteratorerna
kan användas på alla containerklasserna utom adapterklasserna
Man kan också använda iteratorena ihop med klassen string eller med strömmar för läsning och skrivning.
Iteratorerna används för:
Den kraftfullaste iteratorn är random-access iterator som kan utföra pekaritmetik
#include
<iostream>
#include
<vector>
#include
<string>
#include
<deque>
using
namespace std;
void
main(){
vector<double>
v(4);
vector<double>::iterator it;
int k =10;
it = v.begin(); //iteratorn pekar på första elementet
it = v.end(); //iteratorn pekar på sista elementet
for(it=v.begin(); it != v.end(); it++)
*it = k++;
for(it=v.begin(); it != v.end(); it++)
cout << *it << endl;
//löpa igenom ett fält med const
vector<double>::const_iterator
cit; //konstant iterator
for(cit=v.begin(); cit != v.end(); cit++)
cout << *cit << endl;
//löpa igenom baklänges reverse
vector<double>::reverse_iterator
rit;
for(rit=v.rbegin (); rit != v.rend(); rit++)
cout << *rit << endl;
//att använda -> för klasstypen string
deque<string> d;
d.push_front("Pascal"); d.push_front("c");
d.push_front("c++"); d.push_front("Java");
for(deque<string>::iterator j=d.begin(); j != d.end(); j++)
{
if
(j->at(0) == 'c')
j->at(0)
= 'C';
cout << *j << " ";
}
}
#include
<iostream>
#include
<vector>
#include
<string>
#include
<deque>
#include
<list>
using
namespace std;
void
main(){
int
a[] = {5,6,7,8,9,10};
vector<int> v(a, a+4); //v innehåller
{5,6,7,8};
list<int> l(v.begin(), v.end()); //l innehåller {5,6,7,8};
//assign finns i form där man ger iteratorer eller pekare som argument
l.assign(a, a+2); //OBS FEL //l innehåller {5,6};
l.insert(l.begin(), 0); //l innehåller {0,5,6};
list<int>::iterator it = l.begin();
l.insert(++it,3,1); //l innehåller {0,1,1,5,6};
//kopierar och skuter in
l.insert(l.begin(), v.begin(), v.end());
//l innehåller {5,6,7,8,0,1,1,5,6};
//ta bort ett element
it = l.begin();
++it,++it,++it;
l.erase (it); //l innehåller {5,6,7,0,1,1,5,6};
//ta bort allt utom det sista
it=l.end();
l.erase (l.begin(),
--it); //l innehåller {6};
l.erase (l.begin() ); //l innehåller {6};
}
Följande typer finns definierade i alla containerklaser. con = container typ(vector eller list)
För klasserna vector och deque är iteratorerna av kategorin random acces iteratorer och övriga bidirectional iterator.
con::iterator<typ> ++ löper framåt
con::const_iterator<typ> ++ löper framåt endast för avläsning (konstant)
con::const_reverse_iterator<typ> ++ löper bakåt endast för avläsning
Följande operationer finns i alla containerklasser samt i klassen string
begin() ger iterator som pekar på första elementet
end() ger iterator som pekar på tänkt element efter det sista tecknet
rbegin ger en baklängesiterator som pekar på sista elementet
rend() ger en baklängesiterator som pekar på elementet före det första
Följande operationer finns för iteratorer av
kategorin forward iterator
(i och j betecknar iteratorer och m en godtycklig medlem i ett objekt av klasstyp)
*i ger det element iteratorn i pekar på (avläsning och utskrift)
i -> samma som (*i).m
++i stegar ett steg framåt
i=j tilldelning
i==j jämförelse (finns ej för output iteratorer)
i!=j jämförelse (olikhet - finns ej för output iteratorer)
För kategorin bidirectional iterator finns alla ovanstående operationer plus följande:
--i stegar ett steg bakåt (ger nya värdet)
i-- stegar ett steg bakåt (ger gamla värdet)
För iteratorer av kategorin random-access finns alla ovanstående operationer + följande (jmf med pekararitmetik)
i += n stegar framiteratorn i n steg
i -= n stegar bak iteratorn i n steg
i + n ger en iterator som motsvarar iteratorn i stegad framåt n steg
i – n ger en iterator som motsvarar iteratorn i stegad bakåt n steg
i = j ger antalet steg mellan iteratorna i och j
i[n] indexering
i < j undersöker om iteratorn i är mindre än j
i > j undersöker om iteratorn i är större än j
i <= j undersöker om iteratorn i är mindre eller lika med j
i >= j undersöker om iteratorn i är större eller lika med j
Sekvenser: Operationer med iteratorer som
parametrar
sekv<typ> s(i,j): skapar en sekvens som initieras med intervallet (i,j)
s.assign(i,j); tilldelar elementen i intervallet (i,j) till s
s.insert(p,e); skjuter in värdet e i pos p
s.insert(p,n,e); skjuter in n stycken e i pos p
s.insert(p,i,j); skjuter in elementen (i,j) i pos p
erase(p); tar bort elementet i pos p
s.erase(p,p2); tar bort elementen i intervallet (p,p2) från s
Många av standardalgoritmerna har som extra parameter ett s.k. funktionsobjekt som beskriver en operation som skall utföras på elementen i datasamlingen. De finns fyra:
//användning av pekare till funktioner sid 440
#include <iostream>
#include <vector>
#include "iodos.h"
#include <list>
#include <deque>
#include <iterator>
#include <algorithm>
#include <cmath>
using namespace std;
void skriv(double x)
{
cout << x << "
";
}
bool udda(int i) //funktionen udda är ett predikat
{
return i % 2 !=0;
}
bool nastan_lika(double
x, double y)
{
return fabs(x-y)
<= 1e-10;
}
int transformera(int x)
{
return x*x;
}
void main(){
dos_console();
cout << "Algoritmer" << endl;
int a[] = {5,6,7,8,9,10,0};
vector<int> v(a,a+4);
deque<int> d(6,0);
//innehåller {0,0,0,0,0,0}
copy(v.begin(),v.end(), d.begin());//d innehåller
{5,6,7,8,0,0}
for(deque<int>::iterator j = d.begin(); j!=d.end(); j++)
cout << *j << " ";
cout << endl;
//byter ut alla udda tal mot 0
replace_if(d.begin(),d.end(),
udda,0);
for(deque<int>::iterator i = d.begin(); i!=d.end(); i++)
cout << *i << " ";
cout << endl;
double x[] =
{3.4,6.7,111111111111 };
double y[] =
{3.4,6.7,111111111112 };
vector<double> vx(x, x+4);
vector<double> vy(y, y+4);
if(equal(vx.begin(),vx.end(), vy.begin (), nastan_lika))
cout << "nästan lika";
else
cout << "inte lika";
cout << endl;
//övriga operationer av funktionsobjekt
for_each(vx.begin (), vx.end(),
skriv);
cout << endl;
//transform
vector<int> v2;
v2.assign(v.begin(),v.end());
cout << endl;
transform(v.begin (), v.end(), v2.begin(), transformera);
for_each(v.begin (), v.end(), skriv);
cout << endl;
for_each(v2.begin
(), v2.end(), skriv);
}
//funktionsobjekt
#include
<iostream>
#include
<vector>
#include
"iodos.h"
#include
<list>
#include
<deque>
#include
<iterator>
#include
<algorithm>
using
namespace std;
class Summerare{
public:
//konstruktor
Summerare(double init=0) : sum(init){}
//funktionsoperator
double operator()
(double x)
{ return sum
+= x;}
private:
double sum;
};
void skriv(double
a)
{
cout << a << " ";
}
void main(){
dos_console();
vector<double>v ;
vector<double>v2
;
for(int i=1;i<13;i++)
{
v.push_back(i);
v2.push_back(i);
}
transform(v2.begin (), v2.end(),v.begin
(), Summerare());
for_each(v.begin
(), v.end(), skriv);
cout << endl;
fill(v.begin(),v.end (),1); //fyller vektorn med ettor
//här ändrar vi i samma datasamling som vi läser från
transform(v.begin(),v.end(),v.begin (), Summerare());
for_each(v.begin (), v.end(),
skriv);
cout << endl;
}
Användning av
fördefinierade funktionsobjekt
#include <functional>
I denna fil finns klasser för olika funktioner som plus, minus, multiplies, divides och negate.
Typen plus kan deklareras som:
plus<double> p;
Eftersom plus funktionen är en mall (template) skall man ange klasstyp. Man kan anropa den som en vanlig funktion cout << p(4,5) << endl; Skriver 9
Fördefinierade funktionsobjekt finns i filen <functional>
Ett temporärt fördefinierat funktionsobjekt skapas med ett uttryck på formen
operator<typ>()
där typ är typen för elementen i dne aktuella datasamlingen
operation kan vara någon av följande (a och b betecknar parametrarna vid ett anrop av funktionsobjektet)
plus ger funktionsobjekt som returnerar a+b
minus ger funktionsobjekt som returnerar a-b
multiplies ger
funktionsobjekt som returnerar a*b
divides ger funktionsobjekt som returnerar a/b
modulus ger funktionsobjekt som returnerar a%b
negate ger funktionsobjekt som returnerar -a negerar (negativt)
equal_to ger
funktionsobjekt som returnerar a==b
not_equal_to ger funktionsobjekt som returnerar a!=b
greater ger funktionsobjekt som returnerar a>b
less ger funktionsobjekt som returnerar a<b
less_equal ger funktionsobjekt som returnerar a<=b
greater_equal ger funktionsobjekt som returnerar a>=b
logical_and ger funktionsobjekt som returnerar a&&b
logical_or ger funktionsobjekt som returnerar a| |b
logical_not ger funktionsobjekt som returnerar ! a
Har funktionsobjekt som parametrar. Ger annat funktionsobjekt som resultat. (Här betecknar f1 och f2 funktionsobjekt med en respektive två parametrar och x ett värde).
bind1st(f2,x) gör om f2 till ett funktionsobjekt med en parameter. Den första parametern är bunden till värdet x.
bind2nd(f2,x) gör om f2 till funktionsobjekt med en parameter. Den andra parametrn är bunden till x.
not1(f1) ger ett funktionsobjekt som vid anrop returnerar det negerade värdet av ett anrop av f1
not2(f2) ger ett funktionsobjekt som vid anrop returnerar det negerade värdet av ett anrop av f2
ptr_fun(f) ger ett funktionsobjekt som vid anrop anropar funktionen f
l.reverse() vänder på listan
l.remove(e) tar bort alla förekomster av e i listan
l.remove(funk) tar bort alla element som uppfyller villkoret funk (är ett funktionsobjekt)
l.unique() tar bort dubletter i listan
l.sort() sorterar listan
l.sort(funkobj) sorterar efter funktiionsobjektet
l.merge(l2) slår samman två listor listorna måste vara sorterade innan merge utförs
l.merge(l2,funkobj) sorterar in listan – funkobj anger hur
l1.splice(p, l2) skjuter elementen i listan l2 i listan l före platsen p
l1.splice(p, l2,i) skjuter elementen i listan l2 i listan l före platsen p. Elementet tas bort ur l2. p och i är iteratorer
l1.splice(p, l2,i,j) skjuter elementen intervallet [i,j] i listan l2 före platsen p ilistan l. Elementen tas bort ur l2. p, i, j är iteratorer
UML
OOP med C++
OOP med C++
Associationer
är fasta samband mellan klasser.
Leta efter verbfraser som
t.ex. äger, arbetar hos, har som anställd, innehar,
etc.
(eng. qualified)
OOP med C++
OOP med C++
OOP med C++
Varför
OOP?
OOP med C++
OOP med C++
OOP
vs. procedurorienterat, metodik
o
varje steg kräver att föregående steg avslutat
o
olika notation i olika steg
o
svårt att ändra i föregående steg
OOP med C++
OOP
vs. procedurorienterat, ramverk
o
_______________
o
|
Huvudprogram | <----
Användarens kod
o
|_______________|
o
| |
|
o
|
| |
o
x x x <---- Bibliotekskod
o
o
_______________
o
| Framework |
<---- Bibliotekskod
o
|_______________|
o
| |
|
o
|
| |
o
x x x <---- Användarens kod
o
OOP med C++
OOP med C++
UML-byggstenar
2.
Strukturer (substantiv)
3.
Beteenden (dynamiska saker, verb)
4.
Grupperingar (organisatoriska saker)
5.
Noteringar (annotional things, förklaringar)
Det finns två sätt… Båda involverar Tools på arkivmenyn.
Om du går på Tools – Costumize och klickar på fliken Tools. Vi skapar en möjlighet att köra programmet härifrån och på så sätt kunna skicka argument…
Om du drar ner rullningslisten längst ner och klickar i det som ser ut som en tomruta får du möjlighet att skriva ett namn… t.ex. MedArgument
Klicka enter
I rutan efter Command söker du reda på din exe fil… Du kan söka dig fram om du klickar på knappen med …
Sen har du två alternativ.
Antingen klickar du i ”Prompt for arguments”, då får du en ruta som frågar efter ditt argument när du kör
Eller så skriver du sökvägen till filen i Arguments…
När du sen ska köra ditt program så kör du det via Tools och väljer MedArgument