background image

 

 

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/ 

background image

 

 

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

background image

 

 

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

background image

 

 

Klasa

class

 

Car

 {

private

:

std::

string

 

brand

;

protected

:

std::

string

 

model

;

public

:

std::

string

 

color

;

void

 setBrand(std::

string

 b);

std::

string

 getBrand();

};

background image

 

 

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"

;

}

background image

 

 

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"

;

}

background image

 

 

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

background image

 

 

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)

background image

 

 

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

background image

 

 

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();

};

background image

 

 

Klasa

int

 main() {

Car

 samochod;

samochod.setBrand(

"Opel"

);

cout << samochod.getBrand() << endl;

// ?

}

Tworzenie obiektów

background image

 

 

Klasa

int

 main() {

Car

 samochod;

samochod.setBrand(

"Opel"

);

cout << samochod.getBrand() << endl;

// Opel

Car

 & osobowy = samochod; // referencja

cout << osobowy.getBrand() << endl;

// ?

}

Tworzenie obiektów

background image

 

 

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

background image

 

 

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

background image

 

 

Klasa

int

 main() {

Car

 samochod;

samochod.setBrand(

"Opel"

);

Car

 * cPtr;

cPtr = &samochod;

cout << cPtr->getBrand() << endl;

// ?

}

Tworzenie obiektów

background image

 

 

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

background image

 

 

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

background image

 

 

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;

background image

 

 

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;

background image

 

 

Klasa

int

 main() {

Car

 * cPtr = 

new

 Car;

cPtr->setBrand(

"Citroen"

);

cout << cPtr->getBrand() << endl;

delete

 cPtr;

}

Dynamiczne tworzenie obiektów

background image

 

 

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

background image

 

 

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ć?

}

background image

 

 

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

background image

 

 

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...

background image

 

 

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...

background image

 

 

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;

// ?

}

background image

 

 

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...

background image

 

 

Klasa

class

 

Person

 {

string

 

name

;

int

 

age

;

bool

 

smoker

;

public

:

Person();

// ...

};

Person::Person() {

age

 = 18;

smoker

 = 

true

;

}

konstruktor

background image

 

 

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...

background image

 

 

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)

background image

 

 

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

background image

 

 

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;

}

background image

 

 

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;

}

background image

 

 

Klasa

class

 

Person

 {

string

 

name

;

int

 

age

;

bool

 

smoker

;

// dalsze składniki ...

};

Abstrahując...

int

 main() {

Person

 p;

p.age = 20;

// czy mogę tak?

}

background image

 

 

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...

background image

 

 

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;

background image

 

 

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;

background image

 

 

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

();

}

}

background image

 

 

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.

background image

 

 

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

background image

 

 

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.

background image

 

 

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();

};

background image

 

 

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)

background image

 

 

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)

background image

 

 

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;

}

background image

 

 

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;

}

background image

 

 

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;

}

background image

 

 

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

background image

 

 

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

background image

 

 

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;

}

background image

 

 

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

background image

 

 

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);

}

background image

 

 

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;

}

background image

 

 

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

background image

 

 

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;

}

background image

 

 

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;

}

background image

 

 

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;

}

background image

 

 

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

;

}

background image

 

 

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

background image

 

 

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

background image

 

 

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.

background image

 

 

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);

background image

 

 

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);

// ...

}

background image

 

 

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!

background image

 

 

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];

}

background image

 

 

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()

background image

 

 

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

}

background image

 

 

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 )

background image

 

 

Klasa

Dziedziczenie „protected”:

class

 

Osobowy

 : 

protected

 

Samochod

 {

// ...

};

- wszystkie składniki publiczne i protected klasy Samochod są w klasie 

Osobowy ustawione na protected

background image

 

 

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

background image

 

 

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.

background image

 

 

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

background image

 

 

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

background image

 

 

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

 

background image

 

 

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();

// ?

}

background image

 

 

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....

background image

 

 

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 !!

background image

 

 

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 !!

background image

 

 

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

}

background image

 

 

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

}

background image

 

 

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)

background image

 

 

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.

background image

 

 

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


Document Outline