Zaawansowane metody
programowania
Źródła:
J. Grębosz
- Symfonia C++
J. Grębosz
- Pasja C++
N.M. Josuttis - C++. Biblioteka standardowa. Podręcznik programisty
Podręcznik C++
- http://pl.wikibooks.org/wiki/C++
C++ bez cholesterolu - http://www.intercon.pl/~sektor/cbx/
Klasa
... to inaczej typ.
Definiując klasy tworzymy własne typy danych wykorzystywanych
w programach.
Klasa posiada składniki, którymi mogą być
- dane
- funkcje
... czyli definiuje zbiór określonych danych oraz wykonywanych na
nich operacji
==> ENKAPSULACJA
Klasa
Etykiety określające dostęp do składników klasy
private:
deklarowane za tą etykietą składniki (dane i funkcje) są
dostępne tylko z wnętrza klasy
protected:
składniki są traktowane jako prywatne z tą różnicą, że
widoczne są także przez klasy wywodzące się z danej klasy
public:
składniki są dostępne bez ograniczeń; najczęściej są nimi
funkcje wywoływane z zewnątrz
Klasa
class
Car
{
private
:
std::
string
brand
;
protected
:
std::
string
model
;
public
:
std::
string
color
;
void
setBrand(std::
string
b);
std::
string
getBrand();
};
Klasa
class
Car
{
private
:
std::
string
brand
;
protected
:
std::
string
model
;
public
:
std::
string
color
;
void
setBrand(std::
string
b);
std::
string
getBrand();
};
// implementacja
int
main() {
Car
c;
c.
brand
=
"Audi"
;
c.
model
=
"A6"
;
c.
color
=
"black"
;
}
Klasa
class
Car
{
private
:
std::
string
brand
;
protected
:
std::
string
model
;
public
:
std::
string
color
;
void
setBrand(std::
string
b);
std::
string
getBrand();
};
// implementacja
int
main() {
Car
c;
// źle
c.
brand
=
"Audi"
;
// źle
c.
model
=
"A6"
;
// OK
c.
color
=
"black"
;
}
Klasa
class
Car
{
private
:
std::
string
brand
;
std::
string
color
;
protected
:
std::
string
model
;
public
:
void
setBrand(std::
string
b);
std::
string
getBrand();
void
setColor(std::
string
c);
std::
string
getColor();
};
Dobre praktyki
- dane składowe
niewidoczne,
- dostęp do nich
za pomocą
publicznych metod
Klasa
class
Car
{
private
:
std::
string
brand
;
std::
string
color
;
protected
:
std::
string
model
;
public
:
void
setBrand(std::
string
b) {
brand
= b;
}
std::
string
getBrand() {
return
brand
;
}
void
setColor(std::
string
c) {
color
= c;
}
std::
string
getColor() {
return
color
;
}
};
Definicja funkcji
wewnątrz
definicji klasy
Jeśli już to tylko
dla „krótkich”
funkcji (1-2 linie
implementacji)
Klasa
class
Car
{
private
:
std::
string
brand
;
std::
string
color
;
protected
:
std::
string
model
;
public
:
void
setBrand(std::
string
b);
std::
string
getBrand();
void
setColor(std::
string
c);
std::
string
getColor();
};
void
Car::setBrand(std::
string
b) {
brand
= b;
}
std::
string
Car::getBrand() {
return
brand
;
}
void
Car::setColor(std::
string
c) {
color
= c;
}
std::
string
Car::getColor() {
return
color
;
}
Definicja klasy
Definicja funkcji
Klasa
class
Car
{
private
:
std::
string
brand
;
std::
string
color
;
protected
:
std::
string
model
;
public
:
void
setBrand(std::
string
b);
std::
string
getBrand();
void
setColor(std::
string
c);
std::
string
getColor();
};
Przestrzeń nazw std::
using
namespace
std;
class
Car
{
private
:
string
brand
;
string
color
;
protected
:
string
model
;
public
:
void
setBrand(
string
b);
string
getBrand();
void
setColor(
string
c);
string
getColor();
};
Klasa
int
main() {
Car
samochod;
samochod.setBrand(
"Opel"
);
cout << samochod.getBrand() << endl;
// ?
}
Tworzenie obiektów
Klasa
int
main() {
Car
samochod;
samochod.setBrand(
"Opel"
);
cout << samochod.getBrand() << endl;
// Opel
Car
& osobowy = samochod; // referencja
cout << osobowy.getBrand() << endl;
// ?
}
Tworzenie obiektów
Klasa
int
main() {
Car
samochod;
samochod.setBrand(
"Opel"
);
cout << samochod.getBrand() << endl;
// Opel
Car
& osobowy = samochod; // referencja
cout << osobowy.getBrand() << endl;
// Opel
osobowy.setBrand(
"Ford"
);
cout << samochod.getBrand() << endl;
// ?
}
Tworzenie obiektów
Klasa
int
main() {
Car
samochod;
samochod.setBrand(
"Opel"
);
cout << samochod.getBrand() << endl;
// Opel
Car
& osobowy = samochod; // referencja
cout << osobowy.getBrand() << endl;
// Opel
osobowy.setBrand(
"Ford"
);
cout << samochod.getBrand() << endl;
// Ford
}
Tworzenie obiektów
Klasa
int
main() {
Car
samochod;
samochod.setBrand(
"Opel"
);
Car
* cPtr;
cPtr = &samochod;
cout << cPtr->getBrand() << endl;
// ?
}
Tworzenie obiektów
Klasa
int
main() {
Car
samochod;
samochod.setBrand(
"Opel"
);
Car
* cPtr;
cPtr = &samochod;
cout << cPtr->getBrand() << endl;
// Opel
cPtr->setBrand(
"VW"
);
cout << samochod.getBrand() << endl;
// ?
}
Tworzenie obiektów
Klasa
int
main() {
Car
samochod;
samochod.setBrand(
"Opel"
);
Car
* cPtr;
cPtr = &samochod;
cout << cPtr->getBrand() << endl;
// Opel
cPtr->setBrand(
"VW"
);
cout << samochod.getBrand() << endl;
// VW
}
Tworzenie obiektów
Klasa
int
main() {
Car
samochod;
samochod.setBrand(
"Opel"
);
// ...
}
Referencje i wskaźniki
// referencja
Car
& osobowy = samochod;
cout << osobowy.getBrand()
<< endl;
// wskaźnik
Car
* cPtr;
cPtr = &samochod;
cout << cPtr->getBrand()
<< endl;
Klasa
int
main() {
Car
samochod;
samochod.setBrand(
"Opel"
);
// ...
}
Referencje i wskaźniki
// referencja
Car
& osobowy = samochod;
cout << osobowy.getBrand()
<< endl;
// wskaźnik
Car
* cPtr;
cPtr = &samochod;
cout << cPtr->getBrand()
<< endl;
// błąd
Car
& osobowy;
osobowy = samochod;
Klasa
int
main() {
Car
* cPtr =
new
Car;
cPtr->setBrand(
"Citroen"
);
cout << cPtr->getBrand() << endl;
delete
cPtr;
}
Dynamiczne tworzenie obiektów
Klasa
int
main() {
Car
* cPtr =
new
Car;
cPtr->setBrand(
"Citroen"
);
cout << cPtr->getBrand() << endl;
delete
cPtr;
int
n = 5;
Car
* tab =
new
Car[n];
// ...
delete
[] tab;
}
Dynamiczne tworzenie obiektów
Klasa
int
main() {
Car
* cPtr =
new
Car;
cPtr->setBrand(
"Citroen"
);
cout << cPtr->getBrand() << endl;
delete
cPtr;
int
n = 5;
Car
* tab =
new
Car[n];
// ...
delete
[] tab;
}
Dynamiczne tworzenie obiektów
Tu warto sprawdzić, czy
przypadkiem nie zabrakło pamięci:
if (tab == NULL) {
// hmm... i co tu robić?
}
Klasa
int
main() {
Car
* cPtr =
new
Car;
cPtr->setBrand(
"Citroen"
);
cout << cPtr->getBrand() << endl;
delete
cPtr;
int
n = 5;
Car
* tab =
new
Car[n];
// ...
delete
[] tab;
}
Dynamiczne tworzenie obiektów
Jeśli uciekło nam
z głowy,
a prowadzący jeszcze tego
nie podkreślił...
W języku C++
zmienne (obiekty)
można deklarować
w (prawie) dowolnym
miejscu w programie,
a nie jedynie
na początku bloku
jak to miało miejsce
w C
Klasa
Odejdźmy od samochodów...
Stwórzmy klasę, która będzie
przechowywać takie
informacje jak nazwisko
delikwenta, jego wiek oraz
fakt bycia (lub nie) palaczem...
Klasa
class
Person
{
string
name
;
int
age
;
bool
smoker
;
public
:
// gettery
string
getName();
int
getAge();
bool
isSmoker();
// settery
// ...
};
Odejdźmy od samochodów...
Stwórzmy klasę, która będzie
przechowywać takie
informacje jak nazwisko
delikwenta, jego wiek oraz
fakt bycia (lub nie) palaczem...
Klasa
class
Person
{
string
name
;
int
age
;
bool
smoker
;
public
:
// gettery
string
getName();
int
getAge();
bool
isSmoker();
// settery
// ...
};
int
main() {
Person
p;
cout << p.getName() << endl;
// ?
cout << p.getAge() << endl;
// ?
cout << p.isSmoker() << endl;
// ?
}
Klasa
class
Person
{
string
name
;
int
age
;
bool
smoker
;
public
:
// gettery
string
getName();
int
getAge();
bool
isSmoker();
// settery
// ...
};
int
main() {
Person
p;
cout << p.getName() << endl;
//
cout << p.getAge() << endl;
// 4072576
cout << p.isSmoker() << endl;
// 48
}
trochę bez sensu...
Klasa
class
Person
{
string
name
;
int
age
;
bool
smoker
;
public
:
Person();
// ...
};
Person::Person() {
age
= 18;
smoker
=
true
;
}
konstruktor
Klasa
class
Person
{
string
name
;
int
age
;
bool
smoker
;
public
:
Person();
// ...
};
Person::Person() {
age
= 18;
smoker
=
true
;
}
int
main() {
Person
p;
cout << p.getName() << endl;
//
cout << p.getAge() << endl;
// 18
cout << p.isSmoker() << endl;
// 1
}
jest lepiej...
Klasa
class
Person
{
string
name
;
int
age = 18
;
bool
smoker = true
;
public
:
// ...
};
Tak nie można!
Inicjalizować składowe dane klasy można tylko w ciele konstruktora lub
na liście inicjalizacyjnej (o tym później)
Klasa
class
Person
{
string
name
;
int
age
;
bool
smoker
;
public
:
Person();
Person(
int
a,
bool
s);
};
Person::Person() {
age
= 18;
smoker
=
true
;
}
// można wprowadzić własne
Person::Person(
int
a,
bool
s) {
age
= a;
smoker
= s;
}
Nadawanie danym składowym wartości w chwili tworzenia obiektów
Klasa
Można pozbyć się konstruktora:
Person();
i pozostawić tylko ten drugi:
Person::Person(
int
a,
bool
s);
nie tracąc poprzedniej funkcjonalności...
...pod warunkiem następującej definicji konstruktora:
Person::Person(
int
a = 18,
bool
s =
false
) {
age
= a;
smoker
= s;
}
Klasa
int
main() {
Person
p1;
// wiek: 18, palacz: nie
Person
p2(21,
true
);
// wiek: 21, palacz: tak
Person
p3(30);
// wiek: 30, palacz: nie
Person
p4(
true
);
// wiek: 1, palacz: nie
}
Przy tak zdefiniowanym konstruktorze:
Person::Person(
int
a = 18,
bool
s =
false
) {
age
= a;
smoker
= s;
}
Klasa
class
Person
{
string
name
;
int
age
;
bool
smoker
;
// dalsze składniki ...
};
Abstrahując...
int
main() {
Person
p;
p.age = 20;
// czy mogę tak?
}
Klasa
class
Person
{
string
name
;
int
age
;
bool
smoker
;
public
:
// brak konstruktorów
// publiczne metody
};
Automatycznie zostanie wygenerowany
domyślny konstruktor (nie przyjmujący
parametrów) nie inicjalizujący żadnych
danych składowych.
Person::Person() {}
I jeszcze ważna uwaga...
Klasa
class
Person
{
string
name
;
int
age
;
bool
smoker
;
public
:
Person::Person(
int
a,
bool
s);
};
Person::Person(
int
a,
bool
s) {
age
= a;
smoker
= s;
}
I jeszcze ważna uwaga...
Jeśli w klasie został zdefiniowany jakiś konstruktor przyjmujący parametry,
domyślny konstruktor (bezargumentowy) nie zostanie automatycznie
wygenerowany. Innymi słowy, w programie nie stworzymy obiektu w ten sposób:
Person
p;
Klasa
class
Person
{
string
name
;
int
age
;
bool
smoker
;
public
:
Person();
Person(
int
a,
bool
s);
};
Person::Person(
int
a,
bool
s) {
age
= a;
smoker
= s;
}
Person::Person() {}
I jeszcze ważna uwaga...
Chcąc mieć taką możliwość (i dane składowe zainicjalizować później za pomocą
metod), należy jawnie zadeklarować bezargumentowy konstruktor. Teraz można:
Person
p;
Klasa
class
Totolotek
{
private
:
int
n
;
int
*
tablicaLiczb
;
public
:
Totolotek(
int
ile);
~Totolotek();
int
* pokazLiczby() {
return
tablicaLiczb
;
}
private
:
void
losuj();
};
Skoro był konstruktor to i powinien... destruktor?
Totolotek::Totolotek(
int
ile) {
n
= ile;
tablicaLiczb
=
new
int
[
n
];
srand
(
time
(NULL));
losuj();
}
Totolotek::~Totolotek() {
delete
[]
tablicaLiczb
;
}
void
Totolotek::losuj() {
for
(
int
i = 0; i <
n
; i++) {
*(
tablicaLiczb
+ i) =
rand
();
}
}
Klasa
Składniki statyczne
Statyczne dane składowe są wspólne dla wszystkich obiektów danej klasy.
Zmiana wartości danych statycznych dowolnego obiektu określonej klasy jest od
razu widoczna przez wszystkie inne obiekty tej klasy.
Klasa
class
Towar
{
private
:
string
nazwa
;
static
float
cena
;
public
:
Towar(
string
n);
void
ustawCene(
float
nowaCena);
float
podajCene();
};
Składniki statyczne
Towar::Towar(
string
n) {
nazwa
= n;
}
void
Towar::ustawCene(
float
nowaCena) {
cena
= nowaCena;
}
void
Towar::podajCene() {
return
cena
;
}
float
Towar::cena
= 5;
Sklep –
wszystko po 5zł
Nadać wartość można w momencie inicjalizacji
Klasa
Składniki statyczne
int
main() {
Towar
t1(
"kubek"
);
Towar
t2(
"ksiazka"
);
Towar
t3(
"flakon"
);
t1.ustawCene(4.50);
cout << t3.podajCene() << endl;
// 4.50
}
Sklep –
wszystko po 5zł
Statyczne dane składowe są wspólne dla wszystkich obiektów danej klasy.
Zmiana wartości danych statycznych dowolnego obiektu określonej klasy jest od
razu widoczna przez wszystkie inne obiekty tej klasy.
Klasa
Składniki statyczne
int
main() {
Towar
::ustawCene(5.50);
Towar
t1(
"kubek"
);
Towar
t3(
"flakon"
);
cout << t3.podajCene()
<< endl;
// 5.50
t1.ustawCene(4.50);
cout << t3.podajCene()
<< endl;
// 4.50
}
Sklep –
wszystko po 5zł
Statyczne funkcje składowe
class
Towar
{
private
:
string
nazwa
;
static
float
cena
;
public
:
Towar(
string
n);
static
void
ustawCene(
float
nowaCena);
float
podajCene();
};
Klasa
Składniki statyczne - przykład
class
Book
{
private
:
string
title
;
static
int
count
;
// ...
public
:
Book();
static
int
getCounter();
// ...
};
int
Book::count
;
Book::Book() {
count
++;
}
int
Book::getCounter() {
return
count
;
}
- konieczna deklaracja obiektu (zmiennej)
statycznego (podobnie jak globalnego)
- statyczna zmienna int jest
inicjalizowana zerem (podobnie jak
zmienna globalna)
Klasa
Składniki statyczne - przykład
int
main() {
for
(
int
i = 0; i < 100; i++) {
Book
b;
}
cout <<
Book
::getCounter()
<< endl;
// 100
}
class
Book
{
private
:
string
title
;
static
int
count
;
// ...
public
:
Book();
static
int
getCounter();
// ...
};
int
Book::count
;
Book::Book() {
count
++;
}
int
Book::getCounter() {
return
count
;
}
- konieczna deklaracja obiektu (zmiennej)
statycznego (podobnie jak globalnego)
- statyczna zmienna int jest
inicjalizowana zerem (podobnie jak
zmienna globalna)
Klasa
Dane składowe const
/**
* Klasa definiujaca
* operacje na kole
*/
class
Kolarz
{
private
:
const
double
pi
;
public
:
Kolarz();
double
podajPole(
double
r);
double
podajObwod(
double
r);
};
double
Kolarz::podajPole(
double
r) {
return
pi
* r * r;
}
double
Kolarz::podajObwod(
double
r) {
return
2 *
pi
* r;
}
Klasa
Dane składowe const
/**
* Klasa definiujaca
* operacje na kole
*/
class
Kolarz
{
private
:
const
double
pi
;
public
:
Kolarz();
double
podajPole(
double
r);
double
podajObwod(
double
r);
};
double
Kolarz::podajPole(
double
r) {
return
pi
* r * r;
}
double
Kolarz::podajObwod(
double
r) {
return
2 *
pi
* r;
}
Kolarz::Kolarz() {
pi
= 3.1415926;
}
Klasa
Dane składowe const
/**
* Klasa definiujaca
* operacje na kole
*/
class
Kolarz
{
private
:
const
double
pi
;
public
:
Kolarz();
double
podajPole(
double
r);
double
podajObwod(
double
r);
};
double
Kolarz::podajPole(
double
r) {
return
pi
* r * r;
}
double
Kolarz::podajObwod(
double
r) {
return
2 *
pi
* r;
}
Kolarz::Kolarz() {
pi
= 3.1415926;
}
Klasa
Dane składowe const
/**
* Klasa definiujaca
* operacje na kole
*/
class
Kolarz
{
private
:
const
double
pi
;
public
:
Kolarz();
double
podajPole(
double
r);
double
podajObwod(
double
r);
};
double
Kolarz::podajPole(
double
r) {
return
pi
* r * r;
}
double
Kolarz::podajObwod(
double
r) {
return
2 *
pi
* r;
}
Kolarz::Kolarz() :
pi
(3.1415926) {
// inicjalizacja obiektów
// nie-const
}
Obiekty nie-const mogą być inicjalizowane również na liście inicjalizacyjnej
Klasa
Wróćmy do samochodów
class
Car
{
private
:
string
brand
;
string
color
;
protected
:
string
model
;
public
:
void
setBrand(
string
b);
string
getBrand();
void
setModel(
string
m);
string
getModel();
void
setColor(
string
c);
string
getColor();
};
Stwórzmy funkcję służącą do
wypisywania na ekranie danych na temat
konkretnego samochodu.
Funkcja taka będzie wymagała
przekazania jej obiektu typu Car.
Obiekt typu Car może zostać przekazany
do funkcji przez:
- wartość
- referencję
- wskaźnik
Klasa
Przekazywanie obiektu do funkcji
- przez wartość
void
wypisz(
Car
c) {
cout << c.getBrand() << endl;
cout << c.getModel() << endl;
cout << c.getColor() << endl;
}
- przez referencję
void
wypisz(
Car
& c) {
cout << c.getBrand() << endl;
cout << c.getModel() << endl;
cout << c.getColor() << endl;
}
- przez wskaźnik
void
wypisz(
Car
* c) {
cout << c->getBrand() << endl;
cout << c->getModel() << endl;
cout << c->getColor() << endl;
}
Klasa
Przekazywanie obiektu do funkcji
- przez wartość
void
wypisz(
Car
c) {
cout << c.getBrand() << endl;
cout << c.getModel() << endl;
cout << c.getColor() << endl;
}
- przez referencję
void
wypisz(
Car
& c) {
cout << c.getBrand() << endl;
cout << c.getModel() << endl;
cout << c.getColor() << endl;
}
- przez wskaźnik
void
wypisz(
Car
* c) {
cout << c->getBrand() << endl;
cout << c->getModel() << endl;
cout << c->getColor() << endl;
}
albo
Klasa
Przekazywanie obiektu do funkcji
int
main() {
// wywolanie w przypadku przekazywania przez
// wartosc lub referencje
Car
c1;
c1.setBrand(
"Audi"
);
c1.setModel(
"A6"
);
c1.setColor(
"black"
);
wypisz(c1);
// wywolanie w przypadku przekazywania przez
// wskaznik
Car
* cPtr = &c1;
wypisz(cPtr);
}
Klasa
Zagadka
void
wypisz(
Car
c) {
c.setBrand("noname");
cout << c.getBrand() << endl;
cout << c.getModel() << endl;
cout << c.getColor() << endl;
}
int
main() {
Car
c1;
c1.setBrand(
"Audi"
);
c1.setModel(
"A6"
);
c1.setColor(
"black"
);
wypisz(c1);
cout << c1.getBrand() << endl;
}
Klasa
Zagadka
int
main() {
Car
c1;
c1.setBrand(
"Audi"
);
c1.setModel(
"A6"
);
c1.setColor(
"black"
);
wypisz(c1);
cout << c1.getBrand() << endl;
}
void
wypisz(
Car
c) {
c.setBrand("noname");
cout << c.getBrand() << endl;
cout << c.getModel() << endl;
cout << c.getColor() << endl;
}
Wynik:
noname
A6
black
Audi
Klasa
Zagadka
void
wypisz(
Car &
c) {
c.setBrand("noname");
cout << c.getBrand() << endl;
cout << c.getModel() << endl;
cout << c.getColor() << endl;
}
int
main() {
Car
c1;
c1.setBrand(
"Audi"
);
c1.setModel(
"A6"
);
c1.setColor(
"black"
);
wypisz(c1);
cout << c1.getBrand() << endl;
}
Klasa
Zagadka
void
wypisz(
Car &
c) {
c.setBrand("noname");
cout << c.getBrand() << endl;
cout << c.getModel() << endl;
cout << c.getColor() << endl;
}
Wynik:
noname
A6
black
noname
int
main() {
Car
c1;
c1.setBrand(
"Audi"
);
c1.setModel(
"A6"
);
c1.setColor(
"black"
);
wypisz(c1);
cout << c1.getBrand() << endl;
}
Klasa
Jeśli chcemy zapewnić użytkownika, że funkcja nie naruszy mu
obiektu, do którego referencję przesyła:
void
wypisz(const
Car &
c) {
//
c.setBrand("noname"); <- tego już kompilator nie puści
cout << c.getBrand() << endl;
cout << c.getModel() << endl;
cout << c.getColor() << endl;
}
Klasa
Jeśli chcemy zapewnić użytkownika, że funkcja nie naruszy mu
obiektu, do którego referencję przesyła:
void
wypisz(const
Car &
c) {
//
c.setBrand("noname"); <- tego już kompilator nie puści
cout << c.getBrand() << endl;
cout << c.getModel() << endl;
cout << c.getColor() << endl;
}
class
Car
{
private
:
string
brand
;
// ...
public
:
string
getBrand()
const
;
// ...
};
std::
string
Car::getBrand()
const
{
return
brand
;
}
Klasa
Jeśli chcemy zapewnić użytkownika, że funkcja nie naruszy mu
obiektu, do którego referencję przesyła:
void
wypisz(const
Car &
c) {
//
c.setBrand("noname"); <- tego już kompilator nie puści
cout << c.getBrand() << endl;
cout << c.getModel() << endl;
cout << c.getColor() << endl;
}
class
Car
{
private
:
string
brand
;
// ...
public
:
string
getBrand()
const
;
// ...
};
std::
string
Car::getBrand()
const
{
return
brand
;
}
Podobnie można zrobić
przy przekazywaniu
przez wskaźnik
Klasa
Konstruktor kopiujący
void
wypisz(
Car
c) {
// ...
}
Przy przekazywaniu obiektu przez
wartość zostaje niejawnie wywołany
domyślny konstruktor kopiujący, który
powiela wartości poszczególnych
zmiennych: brand, model, color
Klasa
Konstruktor kopiujący
void
wypisz(
Car
c) {
// ...
}
Przy przekazywaniu obiektu przez
wartość zostaje niejawnie wywołany
domyślny konstruktor kopiujący, który
powiela wartości poszczególnych
zmiennych: brand, model, color
Car funkcja() {
Car c;
// ...
return c;
}
Podobnie rzecz wygląda w momencie
zwracania obiektu przez funkcje poprzez
return. Obiekt lokalny jest tu kopiowany,
jego kopia jest widoczna w miejscu
wywołania funkcji w programie.
Klasa
Konstruktor kopiujący
Konstruktor kopiujący możemy wywołać też jawnie:
Wynik:
Audi
A6
black
Car
c1;
c1.setBrand(
"Audi"
);
c1.setModel(
"A6"
);
c1.setColor(
"black"
);
Car
c2(c1);
wypisz(c2);
Klasa
Konstruktor kopiujący - problem
class
Totolotek
{
private
:
int
n
;
int
*
tablicaLiczb
;
public
:
Totolotek(
int
ile);
// ...
public
:
void
losuj();
};
Totolotek::Totolotek(
int
ile) {
n
= ile;
tablicaLiczb
=
new
int
[
n
];
srand
(
time
(NULL));
losuj();
}
int
main() {
Totolotek
losowanie(6);
Totolotek
kopia(losowanie);
// ...
}
Klasa
Konstruktor kopiujący - problem
class
Totolotek
{
private
:
int
n
;
int
*
tablicaLiczb
;
public
:
Totolotek(
int
ile);
// ...
public
:
void
losuj();
};
Totolotek::Totolotek(
int
ile) {
n
= ile;
tablicaLiczb
=
new
int
[
n
];
srand
(
time
(NULL));
losuj();
}
int
main() {
Totolotek
losowanie(6);
Totolotek
kopia(losowanie);
// ...
}
- wyświetlając wylosowane elementy z
tablicyLiczb każdego z obiektów
(losowanie i kopia) dostajemy te same
liczby -> OK
- po ponownym wywołaniu metody losuj()
na obiekcie losowanie, liczby zawarte w
obiekcie kopia są TAKIE SAME jak w
obiekcie losowanie -> Nie OK!
Klasa
Należy zdefiniować konstruktor kopiujący
class
Totolotek
{
private
:
int
n
;
int
*
tablicaLiczb
;
public
:
Totolotek(
int
ile);
Totolotek(
Totolotek
& t)
// ...
public
:
void
losuj();
};
Totolotek::Totolotek(
Totolotek
& t) {
n
= t.
n
;
tablicaLiczb
=
new
int
[
n
];
for
(
int
i=0; i<
n
; i++)
tablicaLiczb
[i] = t.
tablicaLiczb
[i];
}
Klasa
Dziedziczenie
class
Samochod
{
public
:
string
marka
;
string
model
;
};
class
Osobowy
:
public
Samochod
{
public
:
int
liczbaOsob
;
};
class
Ciezarowy
:
public
Samochod
{
public
:
float
ladownosc
;
};
class
Wywrotka
:
public
Ciezarowy
{
public
:
void
wysypLadunek();
};
Samochód
- marka
- model
Osobowy
- liczbaOsób
Ciężarowy
- ładowność
Wywrotka
- wysypŁadunek()
Klasa
Składniki klasy
podstawowej
Widoczność w klasach pochodnych
Widoczność w programie
private
class
Samochod
{
private
:
int
marka
;
void
wysMarke();
}
niewidoczne
class
Osobowy
:
public
Samochod
{
public
:
void
f() {
wysMarke();
// ŹLE
}
};
niewidoczne
int
main() {
Samochod
s;
s.wysMarke();
// ŹLE
}
protected
class
Samochod
{
private
:
int
marka
;
protected
:
void
wysMarke();
}
widoczne
class
Osobowy
:
public
Samochod
{
public
:
void
f() {
wysMarke();
// OK
}
};
niewidoczne
int
main() {
Samochod
s;
s.wysMarke();
// ŹLE
}
public
class
Samochod
{
private
:
int
marka
;
public
:
int
wysMarke();
}
widoczne
class
Osobowy
:
public
Samochod
{
public
:
void
f() {
wysMarke();
// OK
}
};
widoczne
int
main() {
Samochod
s;
s.wysMarke();
// OK
}
Klasa
Składniki klasy
podstawowej
Widoczność w klasach pochodnych
Widoczność w programie
private
class
Samochod
{
private
:
int
marka
;
void
wysMarke();
}
niewidoczne
class
Osobowy
:
public
Samochod
{
public
:
void
f() {
wysMarke();
// ŹLE
}
};
niewidoczne
int
main() {
Samochod
s;
s.wysMarke();
// ŹLE
}
protected
class
Samochod
{
private
:
int
marka
;
protected
:
void
wysMarke();
}
widoczne
class
Osobowy
:
public
Samochod
{
public
:
void
f() {
wysMarke();
// OK
}
};
niewidoczne
int
main() {
Samochod
s;
s.wysMarke();
// ŹLE
}
public
class
Samochod
{
private
:
int
marka
;
public
:
int
wysMarke();
}
widoczne
class
Osobowy
:
public
Samochod
{
public
:
void
f() {
wysMarke();
// OK
}
};
widoczne
int
main() {
Osobowy
o;
o.wysMarke();
// OK
}
Oczywiście można też tak
- publiczna metoda klasy
podstawowej może być
wywoływana na obiekcie
klasy pochodnej
( dotyczy to tylko
dziedziczenia publicznego )
Klasa
Dziedziczenie „protected”:
class
Osobowy
:
protected
Samochod
{
// ...
};
- wszystkie składniki publiczne i protected klasy Samochod są w klasie
Osobowy ustawione na protected
Klasa
Dziedziczenie „protected”:
class
Osobowy
:
protected
Samochod
{
// ...
};
- wszystkie składniki publiczne i protected klasy Samochod są w klasie
Osobowy ustawione na protected
Dziedziczenie „private”:
class
Osobowy
:
private
Samochod
{
// ...
};
- wszystkie składniki publiczne i protected klasy Samochod są w klasie
Osobowy ustawione na private
Klasa
Dziedziczenie
Nie dziedziczą się:
- konstruktor
- destruktor
- operator przypisania
W momencie tworzenia obiektu klasy pochodnej, najpierw wywoływany jest
konstruktor klasy podstawowej (domyślny jeśli na liście inicjalizacyjnej nie
wskażemy inaczej), później konstruktor klasy pochodnej.
W momencie likwidowania obiektu klasy pochodnej, najpierw wywoływany jest
destruktor klasy pochodnej, a po nim klasy podstawowej.
class
Person
{
string
name
;
int
age
;
public
:
Person();
Person(
string
name,
int
age);
// ...
};
Klasa
class
Worker
:
public
Person
{
string
occ
;
public
:
Worker();
Worker(
string
name,
int
age,
string
occ);
};
Worker::Worker() : Person() {}
Worker::Worker(
string
p_name,
int
p_age,
string
p_occ) : Person(p_name, p_age) {
occ
= p_occ;
}
Person::Person() {
age
= 18;
}
Person::Person(
string
p_name,
int
p_age) {
name
= p_name;
age
= p_age;
}
Konstruktory klasy
podstawowej
class
Person
{
string
name
;
int
age
;
public
:
Person();
Person(
string
name,
int
age);
// ...
};
Klasa
class
Worker
:
public
Person
{
string
occ
;
public
:
Worker();
Worker(
string
name,
int
age,
string
occ);
};
Worker::Worker() {}
Worker::Worker(
string
p_name,
int
p_age,
string
p_occ) : Person(p_name, p_age) {
occ
= p_occ;
}
Person::Person() {
age
= 18;
}
Person::Person(
string
p_name,
int
p_age) {
name
= p_name;
age
= p_age;
}
Konstruktor klasy
podstawowej
Klasa
class
Totolotek
{
private
:
int
n
;
int
*
tablicaLiczb
;
public
:
Totolotek(
int
ile);
Totolotek(
Totolotek
& t)
// ...
public
:
void
losuj();
};
class
DuzyLotek
:
public
Totolotek
{
private
:
int
zakres
;
public
:
DuzyLotek();
public
:
void
losuj(); // do przedefiniowania
};
DuzyLotek::DuzyLotek() : Totolotek(6) {
zakres
= 49;
}
Jeśli w klasie podstawowej nie ma konstruktora domyślnego,
trzeba jawnie wskazać, który ma zostać wywołany
Klasa
class
Instrument
{
public
:
void
graj() {
cout <<
"nie wiem jak"
<< endl;
}
};
class
Flet
:
public
Instrument
{
public
:
void
graj() {
cout <<
"pipipi"
<< endl;
}
};
Funkcje wirtualne... za chwilę
int
main() {
Instrument
* iPtr =
new
Instrument;
iPtr->graj();
// ?
Flet
* fPtr =
new
Flet;
fPtr->graj();
// ?
Instrument
* i2Ptr =
new
Flet;
i2Ptr->graj();
// ?
}
Klasa
class
Instrument
{
public
:
void
graj() {
cout <<
"nie wiem jak"
<< endl;
}
};
class
Flet
:
public
Instrument
{
public
:
void
graj() {
cout <<
"pipipi"
<< endl;
}
};
Funkcje wirtualne... za chwilę
int
main() {
Instrument
* iPtr =
new
Instrument;
iPtr->graj();
// nie wiem jak
Flet
* fPtr =
new
Flet;
fPtr->graj();
// pipipi
Instrument
* i2Ptr =
new
Flet;
i2Ptr->graj();
// nie wiem jak
}
hmm....
Klasa
class
Instrument
{
public
:
void virtual
graj() {
cout <<
"nie wiem jak"
<< endl;
}
};
class
Flet
:
public
Instrument
{
public
:
void
graj() {
cout <<
"pipipi"
<< endl;
}
};
Funkcje wirtualne
int
main() {
Instrument
* iPtr =
new
Instrument;
iPtr->graj();
// nie wiem jak
Flet
* fPtr =
new
Flet;
fPtr->graj();
// pipipi
Instrument
* i2Ptr =
new
Flet;
i2Ptr->graj();
// pipipi
}
juhu !!
Klasa
class
Instrument
{
public
:
void virtual
graj() {
cout <<
"nie wiem jak"
<< endl;
}
};
class
Flet
:
public
Instrument
{
public
:
void
graj() {
cout <<
"pipipi"
<< endl;
}
};
Funkcje wirtualne
int
main() {
Instrument
* iPtr =
new
Instrument;
iPtr->graj();
// nie wiem jak
Flet
* fPtr =
new
Flet;
fPtr->graj();
// pipipi
Instrument
* i2Ptr =
new
Flet;
i2Ptr->graj();
// pipipi
}
juhu !!
Klasa
class
Instrument
{
public
:
void virtual
graj() {
cout <<
"nie wiem jak"
<< endl;
}
};
class
Flet
:
public
Instrument
{
public
:
void
graj() {
cout <<
"pipipi"
<< endl;
}
};
Funkcje wirtualne – inny przykład
void
zaprezentujSie(
Instrument
& i) {
i.graj();
}
int
main() {
Instrument
i;
Flet
f;
zaprezentujSie(i);
// nie wiem jak
zaprezentujSie(f);
// pipipi
}
Klasa
class
Instrument
{
private:
string
nazwa;
public
:
// setter i getter dla nazwy
void virtual
graj() = 0;
};
class
Flet
:
public
Instrument
{
public
:
void
graj() {
cout <<
"pipipi"
<< endl;
}
};
Funkcje wirtualne – inny przykład
Czy potrzebny nam obiekt typu Instrument, skoro i tak nie umie grać?
Klasa Instrument może definiować pewne cechy wspólne (np. nazwę instrumentu).
int
main() {
Instrument
* iPtr =
new
Instrument;
// PROTEST KOMPILATORA
Flet
* fPtr =
new
Flet;
fPtr->graj();
// pipipi
Instrument
* i2Ptr =
new
Flet;
i2Ptr->graj();
// pipipi
}
Klasa
class
Instrument
{
private:
string
nazwa;
public
:
// setter i getter dla nazwy
void virtual
graj() = 0;
};
class
Flet
:
public
Instrument
{
public
:
void
graj() {
cout <<
"pipipi"
<< endl;
}
};
Funkcje wirtualne – inny przykład
Czy potrzebny nam obiekt typu Instrument, skoro i tak nie umie grać?
Klasa Instrument może definiować pewne cechy wspólne (np. nazwę instrumentu).
Funkcja czysto wirtualna
Klasa abstrakcyjna
(nie można stworzyć
obiektu tej klasy)
Klasa
class
Instrument
{
private:
string
nazwa;
public
:
// setter i getter dla nazwy
void virtual
graj() = 0;
};
class
Flet
:
public
Instrument
{
public
:
void
graj() {
cout <<
"pipipi"
<< endl;
}
};
Funkcje wirtualne – inny przykład
Czy potrzebny nam obiekt typu Instrument, skoro i tak nie umie grać?
Klasa Instrument może definiować pewne cechy wspólne (np. nazwę instrumentu).
Funkcja czysto wirtualna
Klasa abstrakcyjna
(nie można stworzyć
obiektu tej klasy)
Może istnieć klasa, której wszystkie
metody są czysto wirtualne.
Taka klasa definiuje tzw.
interfejs.
Klasa
class
Instrument
{
private:
string
nazwa;
public
:
// setter i getter dla nazwy
void virtual
graj() {
// implementacja
}
virtual
~Instrument() {
// implementacja
}
};
Wirtualny destruktor
Jeśli określona klasa definiuje choć jedną metodę wirtualną, jej destruktor także
powinien być wirtualny.
Wirtualny destruktor