Informatyka jezyki programowania Wyklad

background image









Programowanie w j

ę

zyku C++








Krzysztof Karbowski

Politechnika Krakowska






Kraków 2003

background image

Krzysztof Karbowski: Programowanie w języku C++

2

Wydanie: 24.10.2009

background image

Krzysztof Karbowski: Programowanie w języku C++

3

Ś

rodowisko Visual Studio 2005.

File – New – Project... :

Rys.1.

Rys.2

background image

Krzysztof Karbowski: Programowanie w języku C++

4

Project – Add New Item …

Rys.3.



Kompilacja programu: „Build – Build Solution” (F7).
Uruchomienie programu: "Debug – Debug Without Debugging" (Ctrl+F5)

background image

Krzysztof Karbowski: Programowanie w j

Kompilator Dev-C++



File – New Project...

Programowanie w języku C++

C++

www.bloodshed.net

5

background image

Krzysztof Karbowski: Programowanie w j


Programowanie w języku C++

6

background image

Krzysztof Karbowski: Programowanie w języku C++

7

1. Pierwszy program.

wariant 1:



#include <iostream>
using namespace
std;
int main()
{

cout << "Pierwszy program \n" ;

return 0;

}


wariant 2:



#include <stdio.h>
int main()
{

printf("Pierwszy program \n") ;

return 0;

}


UWAGA:

C++ jest językiem o wolnym formacie zapisu. Poza nielicznymi
wyjątkami instrukcje można zapisywać w dowolnym miejscu. Koniec
instrukcji jest oznaczany znakiem „ ; ”.


wariant 3:



#include <stdio.h>
main(){ printf("Pierwszy program \n"); return 0;}


Kompilacja i konsolidacja programu:

1.

kompilator

2.

linker

2. Drugi program



/* --------------------------------------------------

Program na przeliczanie wysokosci podanej w stopach
na wysokosc w metrach.
Opearacje wejscia/wyjscia.
---------------------------------------------------- */
#include <iostream>
using namespace
std;
int main()
{
int
stopy;

// definicja zmiennej

float metry;

// definicja zmiennej

float przelicznik=0.3;

// def. i inicjalizacja zmiennej

cout << "Podaj wysokosc w stopach: " ;

cin >> stopy ;

// przyjecie danej z klawiatury

metry = stopy * przelicznik ;

cout << endl ;

// wypisanie wynikow

background image

Krzysztof Karbowski: Programowanie w języku C++

8

cout << stopy << " stop to " << metry

<< " metrow \n" ;

return 0;

}

3. Instrukcje steruj

ą

ce.

3.1. Prawda – fałsz

wartość zero

-

odpowiada stanowi: fałsz (FALSE)

wartość inna niż zero -

odpowiada stanowi: prawda (TRUE)

3.2. Instrukcja warunkowa if

Forma 1:

if (wyrażenie) instrukcja1 ;

Forma 2:

if (wyrażenie) instrukcja1 ;

else

instrukcja2 ;


Przykład:

int i ;
cout << Podaj liczbe: “ ;
cin >> i ;
if (i – 4) cout << "liczba rozna od 4" ;
else cout << "liczba rowna 4" ;

Wybór wielowariantowy:

if (wyrażenie1)

instrukcja1 ;

else if (warunek2)

instrukcja2 ;

else if (warunek3)

instrukcja3 ;

3.3. Blok instrukcji

{

instrukcja1 ;

instrukcja2 ;

instrukcjan ;

}


Przykład:

int i ;
cout << Podaj liczbe: “ ;
cin >> i ;
if (i < 5)
{

cout << "liczba mniejsza od 5" ;

cout << endl ;

i = 5 ;

} // tu NIE MA srednika
else
{

cout << "Liczba nie jest mniejsza od 5" ;

cout << endl ;

}

background image

Krzysztof Karbowski: Programowanie w języku C++

9

3.4. Instrukcja while

while (wyrażenie) instrukcja ;

Działanie:

Wykonuj instrukcję tak długo jak długo wartość wyrażenia jest niezerowa. Warunek

sprawdzany jest przed wykonaniem instrukcji.

Przykład:



#include <iostream>
using namespace
std;
int main()
{
int
ile ;

cout << "podaj ilosc znakow: " ;

cin >> ile ;

// narysuj znaki x

while (ile)

{

cout << "x" ;

ile = ile - 1 ;

}

cout << endl ;

return 0;

}

3.5. P

ę

tla do ... while ...

do instrukcja1 while (wyrażenie) ;

Działanie:

Wykonuj instrukcję tak długo jak długo wartość wyrażenia jest niezerowa. Warunek

sprawdzany jest po wykonaniu instrukcji.

Przykład:



#include <iostream>
using namespace
std;
int main()
{
char
litera;

do

{

cout << "Podaj litere: " ;

cin >> litera ;

cout << "\nNapisales: " << litera << endl ;

} while (litera != ‘k’);

cout << "koniec petli\n" ;

return 0;

}

3.6. P

ę

tla for

for (instrukcja_inicjalizująca ; wyrażenie_warunkowe ; instrukcja_kroku) instrukcja ;


Przykład:

background image

Krzysztof Karbowski: Programowanie w języku C++

10

for (i = 0 ; i < 10 ; i=i+1)
{

cout << "*" ;

}

Działanie:

1.

Wykonywana jest instrukcja inicjalizująca pętli.

2.

Obliczane jest wyrażenie warunkowe. Jeśli jest równe 0 to praca pętli jest przerywana.

3.

Jeżeli wyrażenie warunkowe jest różne od zera, to wykonywane są instrukcje będące
treścią pętli.

4.

Po wykonaniu treści pętli wykonywana jest instrukcja kroku, po czym powtarzana jest
akcja 2.

Przypadki szczególne:

Instrukcja inicjalizująca nie musi być jedną instrukcją. Może być ich kilka, wtedy
oddzielone są przecinkami. Podobnie jest w przypadku instrukcji kroku

Elementy instrukcja_inicjalizująca , wyrażenie_warunkowe oraz instrukcja_kroku nie
muszą wystąpić. Dowolny z nich można opuścić, zachowując średnik oddzielający go
od sąsiada. Opuszczenie wyrażenia warunkowego traktowane jest tak, jakby stało tam
wyrażenie zawsze prawdziwe.

Przykład

:

#include <iostream>
using namespace std;
int main()
{
int
i, ile ;

cout << "Podaj liczbe: " ;

cin >> ile ;

for (i=1; i<=ile; i=i+1)

{

cout << i << ". -*-" ;

cout << endl ;

}

cout << "koniec petli\n" ;

return 0;

}

3.7. Instrukcja switch

switch (wyrażenie)
{
case wart1:

intrukcja1 ;

case wart2:

intrukcja2 ;

default:

instrukcja3 ;

}

Przykład

:

#include <iostream>
using namespace std;
// rozpoznawanie cyfr
int main()
{

background image

Krzysztof Karbowski: Programowanie w języku C++

11

int liczba;

cout << "Podaj liczbe: ";

cin >> liczba;

switch (liczba)

{

case 1:

cout << "jeden" << endl;

break;

case 2:

cout << "dwa" << endl;

// tu nie ma break

case 3:

cout << "trzy" << endl;

break;

default:

cout << "nie znam" << endl;

break;

}

return 0;

}

3.8. Instrukcja break

Instrukcja break przerywa działanie pętli:

for

while

do ... while

Przykład:

int i=7;
while (1)
{

i=i-1;

if (i<5) break;

}

3.9. Instrukcja goto

goto etykieta ;

Skok do miejsca w programie opatrzonego etykietą.
Przykład:

cout << "tekst 1" ; //zostanie wyswietlony
cout << "tekst 2" ; //zostanie wyswietlony
goto etykieta1;
cout << "tekst 3" ; //nie zostanie wyswietlony

etykieta1:

cout << "tekst 4" ; //zostanie wyswietlony

3.10. Instrukcja continue

Instrukcja continue działa wewnątrz pętli:

for

while

do ... while

background image

Krzysztof Karbowski: Programowanie w języku C++

12

i powoduje zaniechanie wykonywania instrukcji wewnątrz pętli ale nie przerywa działania
pętli.

Przykład:

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

cout << i;

if (i>5) continue;

cout << endl;

}
cout << endl;


Inaczej można by zapisać:

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

cout << i;

if (i>5) goto koniec;

cout << endl;

koniec:

}
cout << endl;

3.11. Klamry w instrukcjach steruj

ą

cych


Przykład 1:

while (i < 4) {

i = i + 1 ;

}

Przykład 2 (Visual Studio 6.0):

while (i < 4)
{

i = i + 1 ;

}

Przykład 3:

while (i < 4)

{
i = i + 1 ;
}

4. Typy

4.1. Deklaracje typów

Deklaracja – informuje kompilator, że dana nazwa reprezentuje obiekt jakiegoś typu, ale nie

rezerwuje dla niego miejsca w pamięci.

Definicja - dodatkowo rezerwuje miejsce w pamięci.

Przykład:

background image

Krzysztof Karbowski: Programowanie w języku C++

13

int a ; //deklaracja i definicja obiektu typu "int" o nazwie "a"
extern int b; //deklaracja obiektu typu "int" o nazwie "b";

//definicja wyst

ą

pi w innym miejscu

4.2. Systematyka typów j

ę

zyka C++

typy fundamentalne

typy pochodne

typy wbudowane

typy zdefiniowane przez użytkownika

4.3. Typy fundamentalne

4.3.1. Typy reprezentuj

ą

ce liczby całkowite

short int

inaczej:

short

int

long int

inaczej:

long

4.3.2. Typ reprezentuj

ą

cy znaki alfanumeryczne

char

4.3.3. Modyfikatory powy

ż

szych typów:

signed

liczba ze znakiem

unsigned

liczba bez znaku (liczba dodatnia)


Przez domniemanie przyjmuje się, iż brak modyfikatora oznacza typ z modyfikatorem signed.

4.3.4. Typy reprezentuj

ą

ce liczby zmiennoprzecinkowe

float

double

long double


Przykłady:

int a ;
short b ;
short int c ;
long double d ;
unsigned int c ;

4.4. Definiowanie obiektów “w biegu”

#include <iostream>
using namespace std;
int main()
{
int a ;

a=1;

int b; // deklaracja/definicja obiektu

background image

Krzysztof Karbowski: Programowanie w języku C++

14

b=a+1

cout << b;

return 0;

}

4.5. Stałe dosłowne

4.5.1. Stałe b

ę

d

ą

ce liczbami całkowitymi:

np.:

17

-33

0

1000

W układzie ósemkowym (zapis rozpoczyna się od cyfry 0):

np.:

010

014

061

W układzie szesnastkowym (zapis rozpoczyna się od 0x):

np.:

0x10

0xa1

0xff


Stałe całkowite traktowane są jak typ int, chyba że reprezentują liczby, które nie zmieściły by
się w typie int – wtedy stała jest typu long.

Wymuszenie typu stałej:

200L

-

stała typu long

277u

-

stała z modyfikatorem unsigned

50uL

-

stała unsigned long

4.5.2. Stałe reprezentuj

ą

ce liczby zmiennoprzecinkowe

12.3

-10.1

10.4e-3

4.5.3. Stałe znakowe

a

7

Znaki specjalne:

\n

-

nowa linia

\t

-

tabulator

\\

-

backslash

\’’

-

apostrof

\"‘

-

cudzysłów

\0

-

znak o kodzie 0 (NULL)

\?

-

znak zapytania

4.5.4. Stałe tekstowe (ła

ń

cuchy tekstowe, stringi)

"To jest ła

ń

cuch tekstowy"

4.6. Typy pochodne

Operatory umożliwiające tworzenie obiektów typów pochodnych:

[]

– tablica obiektów danego typu

*

– wskaźnik do obiektu danego typu

()

– funkcja zwracająca wartość danego typu

&

– referencja obiektu danego typu

Przykłady:

int t[10] ;

// tablica 10 elementów typu int

float *p ;

// wska

ź

nik do obiektu typu float

char func() ;

//funkcja zwracaj

ą

ca obiekt typu char

background image

Krzysztof Karbowski: Programowanie w języku C++

15

5.6.1. Typ void

void *p ;

- oznacza, że p jest wskaźnikiem do obiektu nieznanego typu

void funkcja();

- funkcja nie zwraca wartości

4.7. Zakres wa

ż

no

ś

ci nazwy obiektu, a czas

ż

ycia obiektu

Czas życia obiektu – okres od momentu zdefiniowania obiektu (przydzielenia miejsca w

pamięci) do momentu, gdy przestaje on istnieć (zwolnienia miejsca w pamięci).

Zakres ważności nazwy obiektu – część programu, w której nazwa znana jest

kompilatorowi.

4.8. Zasłanianie nazw

Przykład

:

#include <iostream>
using namespace std;
int k=33; // zmienna globalna
int main()
{

cout << "main (przed blokiem): k=" << k << endl;

{ //poczatek bloku: zakres lokalny

int k=1;

cout << "blok: k=" << k << endl;

cout << "dostep do zmiennej globalnej k=" <<

::k << endl;

} //koniec bloku

cout << "main (za blokiem): k=" << k << endl;

return 0;

}

4.9. Modyfikator const

Służy do tworzenia obiektów stałych, których wartości nie da się zmienić.

const float pi=3.1415927;

Wartości tak zainicjalizowanego obiektu nie można zmienić.

Inicjalizacja – nadanie wartości obiektowi w momencie jego utworzenia.
Przypisanie – wstawienie do obiektu wartości w jakimkolwiek późniejszym momencie.

Obiekty const można inicjalizować, ale nie można do nich nic przypisać.

4.10. Instrukcja typedef

Pozwala na nadanie dodatkowej nazwy już istniejącemu typowi.

typedef int cena ;
typedef char * napis ;
cena x;

//co odpowiada: int x;

napis komunikat;

//co odpowiada: char * komunikat;

4.11. Typy wyliczeniowe enum

enum nazwa_typu {lista_wyliczeniowa} ;

Przykład:

enum liczby { zero, jeden, dwa, trzy };
enum liczby2
{

background image

Krzysztof Karbowski: Programowanie w języku C++

16

jedenascie=11,

trzynascie=13,

dwadziescia=20

};
liczby a;

//definicja zmiennej

liczby2 b;

//definicja zmiennej


a=zero;

b=dwadziescia;

5. Operatory

5.1. Operatory arytmetyczne

+

dodawanie

-

odejmowanie

*

mnożenie

/

dzielenie

Przykład:

a = b + c ;
a = b – c ;
a = b * c ;
a = b / c ;
a = (c + d + 3.1) / f ;

5.1.1. Operator modulo

Oblicza resztę z dzielenia

a = b % c ;

5.1.2. Jednoargumentowe operatory + i –

Przykład:

+12.7 -x -(a*b)

5.1.3. Operatory inkrementacji i dekrementacji

i = i + 1 ;

można zastąpić

i++ ;

k = k – 1;

można zastąpić

k--;

Formy operatorów:

przedrostkowa (prefix):

++a

lub

--b .

Najpierw zmieniana jest wartość zmiennej, a

następnie zmieniona wartośc staje się wartością wyrażenia.

końcówkowa (postfix):

a++

lub

b-- .

Wartość zmiennej staje się wartością wyrażenia, a

następnie zmieniana jest wartość zmiennej.

Przykład:

int a=0;
int b=0;
cout << ++a << endl; // pojawi si

ę

1

cout << b++ << endl; // pojawi si

ę

0

cout << a << endl; // pojawi si

ę

1

cout << b << endl; // pojawi si

ę

1

5.1.4. Operator przypisania =

m = 27.9

background image

Krzysztof Karbowski: Programowanie w języku C++

17

Do obiektu stającego po lewej stronie operatora = wstawiona zostaje wartość wyrażenia
stojącego po prawej.

5.2. Operatory logiczne

5.2.1. Operatory relacji

<

mniejszy niż ...

<=

mniejszy lub równy

>

większy niż ...

>=

większy lub równy

!=

różny

= =

równy

Przykład

 :

#include <iostream>
using namespace std;
int main()
{
int a=10;
int b=5;

cout << "a=" << a << " b=" << b << endl;

if (a=b)

// tu powinno byc a==b

cout << "a=b" << endl;

else

cout << "a!=b" << endl;

return 0;

}

Powyższy program wyświetli informacje, że a=b, gdyż w wyrażeniu warunkowym instrukcji
if znajduje się instrukcja przypisania a nie operator relacji. Instrukcja przypisania jest
wyrażeniem, a więc ma wartość (wartość wyrażenia po prawej stronie znaku =). W tym
przypadku jest to wartość 5, która jest różna od zera, a więc zostanie zinterpretowana jako
"prawda".

5.2.2. Operatory sumy i iloczynu logicznego

||

suma logiczna – operacja logiczna "lub" (alternatywa)

&&

iloczyn logiczny – operacja logiczna "i" (koniunkcja)

Przykład:

int k=2;
if ((k==10) || (k==2))

cout << "k równe 2 lub 10 \n" ;

if ((k>=0) && (k<=10))

cout << "k nalezy do przedzialu [0,10] \n" ;

5.2.3. Operator negacji logicznej

! wyrażenie

Przykład:

int i = 0 ;
if (!i)

cout << "tekst\n" ;

background image

Krzysztof Karbowski: Programowanie w języku C++

18

5.3. Operatory bitowe

zmienna

<<

ile_miejsc

przesunięcie w lewo

zmienna

>>

ile_miejsc

przesunięcie w prawo

zmienna1

&

zmienna2

bitowy iloczyn logiczny (AND)

zmienna1

|

zmienna2

bitowa suma logiczna (OR)

zmienna1

^

zmienna2

bitowa różnica symetryczna (XOR)

~

zmienna

bitowa negacja

5.4. Pozostałe operatory przypisania

operator

zamiennik

i = i + 2 i += 2
i = i – 2 i -= 2
i = i * 2 i *= 2
i = i / 2 i /= 2
i = i % 2 i %= 2
i = i >> 2 i >>= 2
i = i << 2 i <<= 2
i = i & 2 i &= 2
i = i | 2 i |= 2
i = i ^ 2 i ^= 2

5.5. Wyra

ż

enie warunkowe

(warunek) ? wartość1 : wartość2

Jeżeli warunek ma wartość niezerową to wartością wyrażenia stanie się wartość1 – w
przeciwnym wypadku wartość2.
Przykład:

int a;
a=5;
c = (a<10) ? 0 : 100 ;

// zmiennej c zostanie przypisane 0

5.6. Operator sizeof

Zwraca rozmiar obiektu lub typu.

sizeof(nazwa_typu)

lub

sizeof(nazwa_obiektu)

Zwrócona liczba jest typu

size_t

(inaczej:

unsigned int

).

Gdy argumentem operatora jest nazwa tablicy statycznej zwraca rozmiar tablicy w bajtach.
Nie jest w stanie określić rozmiaru tablicy alokowanej dynamicznie oraz tablicy będącej
parametrem formalnym funkcji.

5.7. Operator rzutowania

(nazwa_typu)obiekt

lub

nazwa_typu(obiekt)

Umożliwia przekształcenie typu obiektu.
Przykład:

int a;
float b=10.1;
a=(int)b;
cout << a << endl;

//zostanie wyswietlona wartosc 10

background image

Krzysztof Karbowski: Programowanie w języku C++

19

5.8. Operator przecinek

Umożliwia grupowanie wyrażeń.

(2+4, a*4, 3<6, 77+2)

Wyrażenia składowe obliczane są od lewej do prawej. Wartością całego wyrażenia staje się
wartość wyrażenia znajdującego się najdalej z prawej strony.

5.9. Programy przykładowe

Przykład

:

Temat: Program dodający dwie liczby rzeczywiste.

#include <iostream>
using namespace std;
int main()
{
float a, b, wynik;

cout << "Podaj pierwsza liczbe: ";

cin >> a;

cout << "Podaj druga liczbe: ";

cin >> b;

wynik=a+b;

cout << "Suma: " << wynik << endl;

return 0;

}


Przykład

:

Temat: Drukowanie alfabetu.

#include <iostream>
int main()
{
char znak;

for (znak=‘a; znak<=‘z; znak++)

cout << znak ;

cout << endl ;

return 0;

}

Przykład

:

Temat: Obliczanie n! .

#include <iostream>
using namespace std;
int main()
{
unsigned long silnia, liczba, licz;

cout << "Podaj liczbe: ";

cin >> liczba;

silnia=1;

licz=2;

if (liczba)

while (licz<=liczba) //for ( ; licz<=liczba; )

{

silnia *= licz;

background image

Krzysztof Karbowski: Programowanie w języku C++

20

licz ++;

}

cout << liczba << "! =" << silnia << endl;

return 0;

}


Przykład

:

Temat: Wyjście z pętli gdy wprowadzona liczba jest równa zero.

#include <iostream>
using namespace std;
int main()
{
int liczba;

do

{

cout << "Podaj liczbe: ";

cin >> liczba;

if (!liczba) cout << "zero - koniec petli\n";

else cout << "To nie jest zero\n";

} while (liczba);

return 0;

}


Przykład

:

Temat: Program pobiera dwie liczby a następnie kod operacji: 0 – koniec; 1 – suma; 2 –

różnica; 3 – iloczyn; 4 – iloraz. Wyświetla wynik obliczeń.

#include <iostream>
int main()
{
float a, b;
int kod;

do

{

cout << "Wprowadz a= ";

cin >> a;

cout << "Wprowadz b= ";

cin >> b;

cout << "0:koniec; 1:(+); 2:(-); 3:(*); 4:(/)." <<

"Podaj kod: ";

cin >> kod;

switch (kod)

{

case 0:

cout << "Koniec pracy\n";

break;

case 1:

cout << a << "+" << b << "=" << a+b << endl;

break;

case 2:

cout << a << "-" << b << "=" << a-b << endl;

break;

background image

Krzysztof Karbowski: Programowanie w języku C++

21

case 3:

cout << a << "*" << b << "=" << a*b << endl;

break;

case 4:

cout << a << "/" << b << "=" << a/b << endl;

break;

default:

cout << "Nieznany kod operacji\n";

break;

}

} while (kod); //while (kod!=0);

return 0;
}

6. Funkcje

Przykład

:

#include <iostream>
using namespace std;
char literki(int ile);

//deklaracja funkcji

//----------------------------------------------
int main()
{
int m;
char znak;

cout << "Podaj ilosc literek: ";

cin >> m;

znak=literki(m);

cout << "Drukowano litere " << znak << endl;

return 0;

}
//----------------------------------------------
// Definicja funkcji
char literki(int ile)
{
int i;

for (i=0; i<ile; i++)

cout << "f";

cout << endl;

return f;

}

6.1. Zwracanie rezultatu przez funkcj

ę

Przykład

: Temat: Program obliczający potęgi liczb całkowitych z podanego przedziału.

#include <iostream>
using namespace std;
long potega(long liczba, int stopien);
void koniec();
//------------------------
int main()
{
long pocz, kon, i;

cout << "Podaj poczatek przedzialu: ";

cin >> pocz;

background image

Krzysztof Karbowski: Programowanie w języku C++

22

cout << "Podaj koniec przedzialu: ";

cin >> kon;

for (i=pocz; i<=kon; i++)

{

cout << i << "^2=" << potega(i,2) << "\t";

cout << i << "^3=" << potega(i,3) << endl;

}

koniec();

return 0;

}
//--------------------------
long potega(long liczba, int stopien)
{
long wynik=liczba;
int i;

for (i=1; i<stopien; i++)

wynik *= liczba;

return wynik;

}//----------------
void koniec()
{

cout << "\nKONIEC PRACY\n";

}

6.2. Przesyłanie argumentów

Rodzaje przesyłania argumentów do funkcji:

przez wartość,

przez referencję,

przez wskaźnik.


Przykład

:

Temat: przesyłanie argumentów przez wartość i przez referencję.

#include <iostream>
using namespace std;
//---------------------------
void zeruj(int a, int &b)
{

cout << "-------------\n";

cout << "Funkcja\n";

cout << "a=" << a << "\tb=" << b << endl;

a=0; b=0;

cout << "Po wyzerowaniu:\n";

cout << "a=" << a << "\tb=" << b << endl;

cout << "-------------\n";

return;

}
//---------------------------
int main()
{
int A=7, B=77;

cout << "Main\n";

cout << "A=" << A << "\tB=" << B << endl;

zeruj(A,B);

cout << "Main\n";

background image

Krzysztof Karbowski: Programowanie w języku C++

23

cout << "Po wywolaniu funkcji:\n";

cout << "A=" << A << "\tB=" << B << endl;

return 0;

}

6.3. Argumenty domniemane

Przykład:
Deklaracje funkcji:

void funkcja1(int arg1, int arg2=0);
void funkcja2(float arg1, int arg2=1, int arg3=5);

Wywołanie funkcji:

funkcja1(10);

funkcja1(10,0);

funkcja1(-10,5);

funkcja2(-2.1,-5);

funkcja2(-2.1,-5,5);

funkcja2(-1, ,3);

tak jest

Ź

LE


Określenie argumentów domniemanych musi wystąpić w deklaracji
funkcji lub w definicji, która jest jednocześnie deklaracją.

6.4. Funkcje inline

Przykład:

inline int dodaj(int a, int b)
{

return a+b;

}

Kompilator utworzy kod, w którym w miejscu wywołania funkcji zostanie wstawiony kod
definiujący funkcję, a nie skok do miejsca definicji funkcji.

Definicja funkcji inline musi znajdować się przed jej wywołaniem, gdyż
w momencie wywołania funkcja musi być znana kompilatorowi – nie
wystarczy deklaracja funkcji.

6.5. Zakresy wa

ż

no

ś

ci nazw deklarowanych wewn

ą

trz funkcji

Zakres ważności nazw deklarowanych w obrębie funkcji ogranicza się tylko do bloku tej
funkcji. Nie można spoza funkcji za pomocą danej nazwy próbować dotrzeć do zmiennej
będącej w obrębie funkcji

Nie można wykonać instrukcji goto spoza funkcji do etykiety zdefiniowanej wewnątrz
funkcji.

6.6. Wybór zakresu wa

ż

no

ś

ci nazwy i czasu

ż

ycia obiektu

6.6.1. Obiekty globalne

Obiekty zadeklarowane na zewnątrz wszystkich funkcji.
Zakres ważności: plik programu.
Czas życia: czas działania programu.
Wartość początkowa: 0

6.6.2. Obiekty automatyczne

Obiekty zadeklarowane wewnątrz bloku programu (np. wewnątrz funkcji, bloku
instrukcji).
Zakres ważności: blok programu.

background image

Krzysztof Karbowski: Programowanie w języku C++

24

Czas życia: od momentu wystąpienia definicji do końca bloku.
Wartość początkowa: przypadkowa.

6.6.2. Obiekty lokalne statyczne

Obiekty zadeklarowane wewnątrz bloku programu (np. wewnątrz funkcji, bloku
instrukcji) i poprzedzone słowem

static

.

Przykład:

static int a;
static float b=2.1;

Zakres ważności: blok programu.
Czas życia: czas działania programu.
Wartość początkowa: 0.

background image

Krzysztof Karbowski: Programowanie w j

6.7. Obiekty w programie składaj

Przykład

 :


Plik main.cpp:

#include <iostream
using namespace std;
#include "funkcja1.h"
#include "funkcja2.h"
int a;

//definicja zmien

int main()
{
int x, y;

cout << "Podaj zmienna globalna a=";

cin >> a;

cout << "Podaj x=";

cin >> x;

cout << "Podaj y=";

cin >> y;

Wypisz_a();

cout << "x+y=" << dodaj(x,y) << endl;

cout << "x-y=" << odejmij(x,y) << endl;

return 0;
}

Plik funkcja1.cpp:

#include <iostream
using namespace std;
extern int a;

//deklaracja zmiennej globalnej

//--------------------------
int dodaj(int a,
{

return a+b;

}
//-------------------------

Programowanie w języku C++

6.7. Obiekty w programie składaj

ą

cym si

ę

z kilku plików

iostream>

std;

#include "funkcja1.h"
#include "funkcja2.h"

//definicja zmiennej globalnej

cout << "Podaj zmienna globalna a=";

cout << "Podaj x=";

cout << "Podaj y=";

cout << "x+y=" << dodaj(x,y) << endl;

y=" << odejmij(x,y) << endl;

iostream>

std;

//deklaracja zmiennej globalnej

--------------------------

a, int b)

-------------------------

25

z kilku plików

background image

Krzysztof Karbowski: Programowanie w języku C++

26

void Wypisz_a()
{

cout << "\nZmienna globalna a=" << a << endl;

}

Plik funkcja2.cpp:

int odejmij(int a, int b)
{

return a-b;

}

Plik funkcja1.h:

int dodaj(int a, int b);
void Wypisz_a();

Plik funkcja2.h:

int odejmij(int a, int b);

6.7.1. Nazwy statyczne globalne

Deklaracje globalne poprzedzone słowem

static

.

Nazwa ta nie będzie znana w innych plikach.

7. Preprocesor

7.1. Dyrektywa #define

#define wyraz ciąg znaków zastępujących go

Kompilator zamieni wszelkie wystąpienia wyrazu ciągiem znaków zastępujących go.

Przykład 1:

#define DWA 2
i=DWA + 2;

Przykład 2:

#define DO_START

do {

#define DO_STOP

}while (!stop);

DO_START

a=a+1;

cin >> stop;

DO_STOP

7.2. Makrodefinicje

#define wyraz(parametry) ciąg znaków zastępujących go

Kompilator zamieni wszelkie wystąpienia wyrazu ciągiem znaków zastępujących go
uwzględniając parametry. Stąd w makrodefinicjach najczęściej występuje nadmiar nawiasów,
aby ustrzec się przed potencjalnymi błędami.

Przykład:

#define KWADRAT(a)

((a) * (a))

wynik=KWADRAT(liczba);

zostanie zamienione na:

wynik=((liczba) * (liczba)) ;

background image

Krzysztof Karbowski: Programowanie w języku C++

27

7.3. Dyrektywy kompilacji warunkowej

#if warunek

linie kompilowane warunkowo

#endif

lub

#if warunek

linie kompilowane warunkowo

#else

linie kompilowane warunkowo

#endif

lub

#ifdef nazwa

linie kompilowane warunkowo

#else

linie kompilowane warunkowo

#endif

lub

#ifndef nazwa

linie kompilowane warunkowo

#else

linie kompilowane warunkowo

#endif


Przykład 1:

#define WARIANT 1
#if (WARIANT==1)

// linie programu

// .............

#else

// linie programu

// .............

#endif //WARIANT==1

7.4. Wstawianie innych plików

#include <nazwa_pliku_1>
#include "nazwa_pliku_2"


Przykład:
Zabezpieczenie przed wielokrotnym dołączeniem pliku:

#ifndef PLIK1_H
#define PLIK1_H

// zwykla tresc pliku

// ..................

#endif //PLIK1_H

7.5. Dyrektywa #undef

Przykład

#define ABC

// zaczyna obowiazywac nazwa ABC

// ...................
// linie programu

background image

Krzysztof Karbowski: Programowanie w języku C++

28

// .....................
#undef ABC

// przestaje obowiazywac nazwa ABC

// .....................

8. Tablice

Definicja tablicy:

typ nazwa[rozmiar];

Rozmiar tablicy musi być stałą znaną w trakcie kompilacji.

Przykłady:

char zdanie[80];

// tablica o nazwie zdanie zawierajaca

// 80 elementów typu char

float numer[9]; // tablica 9 elementow typu float
int *wskaz[20]; // tablica 20 element bedacych wskaznikami

// obiektow typu int

Tablice można tworzyć z:

typów fundamentalnych (za wyjątkiem void),

typów wyliczeniowych (enum),

wskaźników,

innych tablic,

z obiektów typu zdefiniowanego przez użytkownika,

ze wskaźników do pokazywania na składniki klasy.

8.1. Elementy tablicy

Tablica:

int t[4];

składa się z następujących elementów:

t[0] t[1] t[2] t[3]


Numeracja elementów tablicy zaczyna się od zera.


Dla zdefiniowanej powyżej tablicy możliwe są m.in. następujące instrukcje przypisania:

t[0]=1;
t[1]=23;
t[2]=-5;
t[3]=10;

Poniższe przypisanie RÓWNIEś ZOSTANIE WYKONANE:

t[4]=1;

Kompilator C++ nie sprawdza zakresu indeksów tablicy


Sposób obliczenia rozmiaru tablicy (nie dotyczy tablicy dynamicznej i parametru formalnego
funkcji):

size_t rozmiar = sizeof(tab)/sizeof(tab[0]);

Przykład

 :

#include <iostream>
using namespace std;
int main()
{
int t[4];

background image

Krzysztof Karbowski: Programowanie w języku C++

29

for (int i=0; i<4; i++)

t[i]=2*i;

for (i=0; i<4; i++)

cout << "t[" << i << "]=" << t[i] << endl;

return 0;

}

8.2. Inicjalizacja tablic

Przykłady:

int tab1[4] = { 17, 5, 4, 200 };
int tab2[4] = { 3,-2 };

// pozostałe elementy zostan

ą

// zainicjalizowane wartoscia 0

int tab3[] = {-3, 1, 6, 8 };

// zostanie zdefiniowana

// tablica "int tab3[4]"

8.3. Przekazywanie tablic do funkcji

Nazwa tablicy jest równocześnie adresem jej zerowego elementu.


Tablice przesyła się podając funkcji tylko adres początku tablicy.
Przykład

:

#include <iostream>
using namespace std;
void dodaj2(int tablica[], int ile);
//----------------------------
int main()
{
const int rozmiar=4;
int tab[rozmiar];

// mo

ż

na wykorzystac zmienna rozmiar

// gdyz jest to zmienna z modyfikatorem

// const

int i;

for (i=0; i<rozmiar; i++)

tab[i]=i;

dodaj2(tab,rozmiar);

for (i=0; i<rozmiar; i++)

cout << "tab[" << i << "]=" << tab[i] << endl;

return 0;

}
//-------------------------------
void dodaj2(int tablica[], int ile)
{
int i;

for (i=0; i<ile; i++)

tablica[i] += 2;

}

Przykład

:

#include <iostream>
using namespace std;
#define ROZMIAR 4
void dodaj2(int tablica[ROZMIAR]);
//----------------------------
int main()
{
int tab[ROZMIAR];

background image

Krzysztof Karbowski: Programowanie w języku C++

30

int i;

for (i=0; i<ROZMIAR; i++)

tab[i]=i;

dodaj2(tab);

for (i=0; i<ROZMIAR; i++)

cout << "tab[" << i << "]=" << tab[i] << endl;

return 0;

}
//-------------------------------
void dodaj2(int tablica[ROZMIAR])
{ // ROZMIAR nie jest konieczny, gdyz funkcja nie korzysta z
tej

// informacji

int i;

for (i=0; i<ROZMIAR; i++)

tablica[i] += 2;

}

W funkcji do określenia rozmiaru tablicy nie można wykorzystać operatora

sizeof

, gdyż na

podstawie parametru formalnego nie da się określić rozmiaru elementu.

8.4. Tablice znakowe


Łańcuch znaków (string) – ciąg znaków alfanumerycznych zakończony znakiem o kodzie 0

(\0 – NULL).

Przykłady tablic znakowych:

char zdanie[80];

// tablica do przechowywania 80 elementów

// b

ę

d

ą

cych znakami.

char sTab[10] = { "lancuch" };

sTab:

l a n c u c h NULL 0 0


Inne sposoby inicjalizacji:

char sTab[10] = { k’, o’, t };

k o t 0 0 0 0 0 0 0

Ponieważ NULL ma kod 0, więc łańcuch zostanie poprawnie zakończony.

char sTab[] = { k’, o’, t };

k o t

Kompilator określi rozmiar tablicy na podstawie liczby elementów na liście

inicjalizacyjnej. Nie ma tam znaku NULL, a więc nie jest to tablica przechowująca łańcuch
znakowy.

char sTab[] = { "kot" };

k o t NULL

Na liście inicjalizacyjnej znajduje się łańcuch znakowy, więc kompilator

automatycznie doda znak NULL.

W powyższy sposób można jedynie inicjalizować tablice. Nie można wykonywać operacji
przypisania.

background image

Krzysztof Karbowski: Programowanie w języku C++

31

Przykład

:

Temat: Funkcja kopiująca łańcuchy znakowe.

#include <iostream>
using namespace std;
void kopiuj(char cel[], char zrodlo[]);
void wypisz(char tab[]);
//----------------------------
int main()
{
char sTab1[] = { "kot"};
char sTab2[10];

cout << "sTab1=";

wypisz(sTab1);

kopiuj(sTab2,sTab1);

cout << "sTab2=";

wypisz(sTab2);

return 0;
}
//------------------------------
void kopiuj(char cel[], char zrodlo[])
{
int i;

for (i=0; ; i++)

{

cel[i]=zrodlo[i];

if (zrodlo[i]==NULL) break;

}

}
//------------------------------
void wypisz(char tab[])
{
int i;

for (i=0; ; i++)

if (tab[i]==NULL) break;

else cout << tab[i];

cout << endl;

}


Przykład

:

Temat: Program wykorzystujący funkcję biblioteczną kopiująca łańcuchy znakowe.

#include <iostream>
#include <string>
using namespace std;
void wypisz(char tab[]);
//----------------------------
int main()
{
char sTab1[] = { "kot"};
char sTab2[10];

cout << "sTab1=";

wypisz(sTab1);

strcpy(sTab2,sTab1);

cout << "sTab2=";

wypisz(sTab2);

return 0;

background image

Krzysztof Karbowski: Programowanie w języku C++

32

}
//----------------------------
void wypisz(char tab[i]) // taka jak w poprzednim programie

8.5. Tablice wielowymiarowe

Są to tablice, których elementami są inne tablice.

int Tab[4][2]; // tablica czterech elementów, z których

// ka

ż

dy jest dwuelementow

ą

tablic

ą

// obiektów typu int

Uwaga: zapis

Tab[i,j]

zostanie zinterpretowany jako

Tab[j]

(występuje operator

"przecinek").

8.5.1. Inicjalizacja tablic wielowymiarowych:

int Tab1[3][2] = { 11, 12, 21, 22, 31, 32 };
int Tab2[3][2] = { {11, 12}, {21, 22}, {31, 32} };

Element Tab1[1,1] jest przesunięty w stosunku do początku tablicy o

(1*2)+1

elementów,

czyli

numer_wiersza*ilo

ść

_kolumn+numer_kolumny

– aby obliczyć jego położenie

musi być znana liczba kolumn tablicy (drugi wymiar) – nie jest konieczna znajomość ilości
wierszy (pierwszy wymiar).

0

1

0

11

12

1

21

22

2

31

32

8.5.2. Przesyłanie tablic wielowymiarowych do funkcji

Aby funkcja mogła obliczyć, gdzie w pamięci znajduje się określony element tablicy – musi
znać liczbę kolumn tablicy.

void funkcja1(float tab[][4]);// funkcja musi zna

ć

liczb

ę

// kolumn

void funkcja2(float tab[][3][5]); // aby obliczy

ć

poło

ż

enie

// elementu
// void funkcja3( float tab[][][]) tak jest

ź

le

9. Wska

ź

niki

Wskaźnik to obiekt do przechowywania informacji o adresie i typie
innego obiektu.

9.1. Definiowanie wska

ź

ników

int * w ;

// w jest wska

ź

nikiem do obiektów typu int

char * pLancuch;

// pLancuch jest wskaznikiem do obiektow typu char

float * pWsk;

// pWsk jest wskaznikiem do obiektow typu float


Zastosowanie wskaźników:

ulepszenie pracy z tablicami,

funkcje mogące zmieniać wartość przysyłanych do nich argumentów,

background image

Krzysztof Karbowski: Programowanie w języku C++

33

dostęp do specjalnych komórek pamięci,

rezerwacja obszarów pamięci.

9.2. Praca ze wska

ź

nikiem

Przykład

:

#include <iostream>
using namespace std;
//----------------------------
int main()
{
int nLiczba=5;
int nLiczba2=10;
int *pInt;
int *pInt2;
int *pInt3;
float Liczba3=1.2;
float *pFloat;
void *pWsk;

cout << "nLiczba=" << nLiczba << endl; // pojawi sie 5

pInt = &nLiczba; // ustawienie wskaznika

cout << "pInt=" << pInt << endl; // pojawi sie adres

cout << "(*pInt)=" << *pInt << endl; // pojawi sie 5

cout << "Przypisanie *pInt=6\n" ;

*pInt = 6 ;

cout << "(*pInt)=" << *pInt << endl; // pojawi sie 6

cout << "nLiczba=" << nLiczba << endl; // pojawi sie 6

cout << "zmiana obiektu wskazywanego\n" ;

pInt = &nLiczba2;

cout << "nLiczba2=" << nLiczba2 << endl; // pojawi sie 10

cout << "(*pInt)=" << *pInt << endl; // pojawi sie 10

// --------------------------------

pFloat = &Liczba3;

cout << "(*pFloat)=" << *pFloat << endl; // pojawi sie 1.2

// --------------------------------

// pFloat=pInt; // <<< - zostanie zasygnalizowany blad

pWsk = pInt;

cout << "pWsk=" << pWsk << endl; // pojawi sie adres

// cout << *pWsk; // <<< - blad

pInt2 = (int*)pWsk; // musi byc rzutowanie

cout << "(*pInt2)=" << *pInt2 << endl; // pojawi sie 10


pFloat = (float*)pInt; // musi byc rzutowanie

cout << "(*pFloat)=" << *pFloat << endl; // pojawi sie

// przypadkowa liczba mimo prawidlowego adresu

pInt3 = (int*)pFloat; // musi byc rzutowanie

cout << "(*pInt3)=" << *pInt3 << endl; // pojawi sie 10

return 0;
}

Wskaźnik do obiektu każdego niestałego typu można przypisać
wskaźnikowi typu void.

background image

Krzysztof Karbowski: Programowanie w języku C++

34

9.3. Zastosowanie wska

ź

ników wobec tablic

Przykład:

int *wsk1, *wsk2;
int tab1[3]={11, 12, 13 };
int tab2[3]={21, 22, 23 };
wsk1 = &tab1[0];
wsk2 = tab2; // nazwa tablicy jest adresem jej pierwszego

// elementu

wsk1 = &tab1[2]; // wskaznik pokazuje na ostatni element

// tablicy

wsk2 = wsk2 + 1; // wskaznik pokazuje na drugi element

// tablicy

Dodanie do wskaźnika liczby całkowitej powoduje, że pokazuje on
o tyle dalszy element tablicy niezależnie od tego jakiego typu są to
elementy (niezależnie od ilości pamięci przeznaczonej na każdy
element).


Przykład

:

#include <iostream>
using namespace std;
int main()
{
int *wsk1, *wsk2;
int tab[10];
int i;

wsk1=tab;

for (i=0; i<10; i++)

*(wsk1+i)=i*10;

cout << "Elementy tablicy:\n";

for (i=0; i<10; i++)

cout << i << ": " << *(wsk1+i) << endl;

cout << endl;

wsk2 = &tab[3];

cout << "3: " << *wsk2 << endl;

wsk2 += 2;

cout << "(3+2): " << *wsk2 << endl;

return 0;

}

9.4. Działania na wska

ź

nikach

(wskaźnik + liczba_całkowita) – przesunięcie wskaźnika o podaną liczbę elementów w przód,
(wskaźnikliczba_całkowita) – przesunięcie wskaźnika o podaną liczbę elementów w tył,
(wskaźnik wskaźnik) – gdy wskaźniki pokazują na elementy tej samej tablicy to wynikiem

będzie liczba dzielących je elementów (liczba dodatnia lub ujemna).

9.5. Porównywanie wska

ź

ników

operator

==

równość wskaźników oznacza, że pokazują na ten sam obiekt,

operator

!=

przeciwieństwo operatora

==

,


Operatory mające zastosowanie do wskaźników pokazujących na elementy tej samej tablicy:

background image

Krzysztof Karbowski: Programowanie w języku C++

35

> < >= <=

9.6. Zastosowanie wska

ź

ników w argumentach funkcji

Przykład

:

#include <iostream>
using namespace std;
void zmien(int *wsk);
//---------------------
int main()
{
int liczba=1;

cout << "Przed wywolaniem funkcji: " << liczba << endl;

zmien(&liczba);

cout << "Po wywolaniu funkcji: " << liczba << endl;

return 0;

}
//---------------------
void zmien(int *wsk)
{

*wsk=100;

}

9.6.1. Odbieranie tablicy jako wska

ź

nika

Przykład

:

#include <iostream>
using namespace std;
void fkc_tab(int tab[], int rozmiar);
void fkc_wsk1(int *wsk, int rozmiar);
void fkc_wsk2(int *wsk, int rozmiar);
//---------------------
int main()
{
int tablica[4] = { 1, 2, 3, 4 };

fkc_tab(tablica,4);

fkc_wsk1(tablica,4);

fkc_wsk2(tablica,4);

cout << endl;

return 0;
}
//---------------------
void fkc_tab(int tab[], int rozmiar)
{

cout << "\nPrzeslano jako tablice\n" ;

for (int i=0; i<rozmiar; i++)

cout << tab[i] << "\t";

}
//---------------------
void fkc_wsk1(int *wsk, int rozmiar)
{

cout << "\nPrzeslano jako wskaznik - wariant 1\n" ;

for (int i=0; i<rozmiar; i++)

cout << *(wsk+i) << "\t";

}
//---------------------
void fkc_wsk2(int *wsk, int rozmiar)

background image

Krzysztof Karbowski: Programowanie w języku C++

36

{

cout << "\nPrzeslano jako wskaznik - wariant 2\n" ;

for (int i=0; i<rozmiar; i++)

cout << wsk[i] << "\t";

}

9.6.2. Argument formalny b

ę

d

ą

cy wska

ź

nikiem do obiektu const

Przykład:

void funkcja(const int *wsk, int rozmiar);


Wskaźnik const pokazuje na obiekty, ale nie pozwala na ich modyfikacje.
Zastosowanie:

1.

Zagwarantowanie, że obiekt wysłany do funkcji poprzez wskaźnik nie zostanie
zmieniony,

2.

Umożliwienie wysłania do funkcji poprzez wskaźnik obiektu stałego (można na niego
pokazać tylko wskaźnikiem do stałej).

9.7. Zastosowanie wska

ź

ników przy dost

ę

pie do konkretnych

komórek pami

ę

ci

Przykład:

wsk = 0x0065fde8;


Wskaźnikowi należy przypisać adres komórki pamięci (sposób adresowania zależny jest od
architektury komputera, systemu operacyjnego i kompilatora).

9.8. Rezerwacja obszarów pami

ę

ci

9.8.1. Operatory new i delete

Przykład:

int *wsk;
wsk = new int;
// ...........
delete wsk;

Operator

new

utworzy nowy obiekt typu

int

, który nie będzie miał nazwy, a jego adres

zostanie przekazany do wskaźnika

wsk

. Operator

delete

zlikwiduje obiekt pokazywany

przez wskaźnik

wsk

.

Dynamiczne tworzenie tablic:

float *wsk;
int rozmiar;
cout << “Podaj rozmiar tablicy: “; cin >> rozmiar;
wsk = new float[rozmiar];
//....
delete [] wsk;

Wstępna inicjalizacja obiektu:

int *wsk;
wsk = new int(12);

Alokacja obiektu w konkretnym miejscu pamięci:

wsk = 0x0065fde8;
wsk2 = wsk new int;


Cechy obiektów utworzonych operatorem

new

:

background image

Krzysztof Karbowski: Programowanie w języku C++

37

obiekty te nie mają nazwy – dostęp do nich odbywa się poprzez wskaźnik,

początkowe wartości obiektów są przypadkowe, chyba że zostały wstępnie
zainicjalizowane,

czas życia: od chwili utworzenia operatorem

new

do momentu usunięcia operatorem

delete

,

zakres ważności: jeśli w danym momencie jest przynajmniej jeden wskaźnik pokazujący
na obiekt, to jest do niego dostęp.

Przykład

 :

#include <iostream>
using namespace std;
int main()
{
int *wsk1, *wsk2;
int i, rozmiar;

cout << "Podaj rozmiar tablicy: ";

cin >> rozmiar;

wsk1 = new int[rozmiar];

if (wsk1==NULL)

cout << "Brak pamieci\n";

else

{

for (i=0; i<rozmiar; i++)

wsk1[i]=i*10;

cout << "Tablica:\n";

for (i=0; i<rozmiar; i++)

cout << i << ": " << wsk1[i] << endl;

wsk2 = new int;

*wsk2=10;

cout << "*wsk2 = " << *wsk2 << endl;

wsk2=wsk1; // utrata dostepu do obiektu

delete [] wsk1;

wsk1=NULL;

//delete wsk2; // niebezpieczenstwo zalamania programu

}

return 0;
}

Wskaźniki definiowane z modyfikatorem

static

inicjalizowane są

wartością

NULL

. Pozostałe wskaźniki mają wartości przypadkowe, a

więc wskazują na przypadkowe obszary pamięci. Próba zapisu takiego
miejsca może spowodować załamanie programu.

9.9. Sposoby inicjalizowania wska

ź

ników


wsk = & obiekt;

wsk = inny_wskaznik;

wsk = tablica;

wsk = funkcja;

background image

Krzysztof Karbowski: Programowanie w języku C++

38

wsk = new int;

wsk = new float[10];

wsk = 0x82a5f2; // adres

wsk = "lancuch tekstowy";

9.10. Tablice wska

ź

ników

Przykład – pięcioelementowa tablica wskaźników do obiektów typu

float

:


float *tabwsk[5]; // inaczej: float *(tabwsk[5]);

Przykład – tablice wskaźników do stringów:


char *tab1[3];

char *tab2[3] = { "jeden", "dwa", "trzy" };

W tablicy

tab2

znajdują się wskaźniki do miejsc w pamięci, w których zlokalizowane są

poszczególne łańcuchy tekstowe.

Przykład

 :

#include <iostream>
using namespace std;
char* dopisz_spacje(const char *wsk1, char *wsk2);
char* dopisz_spacje2(const char *wsk1, char **wsk2);
void kody(const char *wsk, int rozmiar);
//--------------------
int main()
{
char tab1[] = { "tablica1" };
char tab2[20] = { 't','a','b','l','i','c','a','2' };
char tab3[] = { 't','a','b','l','i','c','a','3','\0' };
char *wsk1 = "wskaznik1";
char *wsk2 = { "wskaznik2" };
char *wsk=NULL;

cout << "tab1: " << tab1 << endl;

wsk = new char[80];

cout << "t a b 1 : " << dopisz_spacje(tab1,wsk) << endl;

delete [] wsk;

wsk=NULL;

cout << "t a b 1 (wersja 2): " << dopisz_spacje2(tab1,&wsk)

<< endl;

delete [] wsk;

cout << "tab2: ";

kody(tab2,20);

cout << "tab3: " << tab3 << endl;

cout << "wsk1: " << wsk1 << endl;

cout << "wsk2: " << wsk2 << endl;

return 0;
}
//--------------------
char* dopisz_spacje(const char *wsk1, char *wsk2)
{

background image

Krzysztof Karbowski: Programowanie w języku C++

39

int ile_znakow, i;

for (ile_znakow=0; *(wsk1+ile_znakow)!=NULL; ile_znakow++)

/* */;

for (i=0; i<ile_znakow; i++)

{

wsk2[2*i] = wsk1[i];

wsk2[2*i+1] = ' ';

}

wsk2[2*ile_znakow]=NULL;

return wsk2;

}
//------------------------
char* dopisz_spacje2(const char *wsk1, char **wsk2)
{
int ile_znakow, i;

for (ile_znakow=0; *(wsk1+ile_znakow)!=NULL; ile_znakow++)

/* */;

*wsk2 = new char[2*ile_znakow+1];

for (i=0; i<ile_znakow; i++)

{

(*wsk2)[2*i] = wsk1[i];

(*wsk2)[2*i+1] = ' ';

}

(*wsk2)[2*ile_znakow]=NULL;

return *wsk2;

}
//----------------------
void kody(const char *wsk, int rozmiar)
{
int i;

for (i=0; i<rozmiar; i++)

cout << int(*(wsk+i)) << ',';

cout << endl;

}

9.11. Wska

ź

niki do funkcji

Definicja:

int (*wsk_do_funkcji)(int, char);


oznacza, że

wsk_do_funkcji

jest wskaźnikiem do funkcji wywoływanej z parametrami

typu

int

i

char

oraz zwracającej wartość typu

int

.

Nazwa funkcji jest jednocześnie adresem jej początku.


Przykład

:

#include <iostream>
using namespace std;
int dodaj_dwa(int a);
int dodaj_trzy(int a);
int wywolaj(int (*wsk)(int), int liczba);

background image

Krzysztof Karbowski: Programowanie w języku C++

40

//----------------------
int main()
{
int a=10;
int (*wsk_fkc)(int parametr);
int (*tab_wsk_fkc[2])(int); // tablica wskaznikow do funkcji

cout << "dodaj_dwa: " << dodaj_dwa(a) << endl;

cout << "dodaj_trzy: " << dodaj_trzy(a) << endl;

wsk_fkc=dodaj_dwa;

cout << "wsk. do dodaj_dwa: " << (*wsk_fkc)(a) << endl;

wsk_fkc=dodaj_trzy;

cout << "wsk. do dodaj_trzy: " << (*wsk_fkc)(a) << endl;

cout << "f(dodaj_trzy): " << wywolaj(dodaj_trzy,a) << endl;

tab_wsk_fkc[0]=dodaj_dwa;

tab_wsk_fkc[1]=dodaj_trzy;

cout << "tablica ze wsk. do dodaj_trzy: "

<< (*tab_wsk_fkc[1])(a) << endl;

return 0;
}
//------------------------
int dodaj_dwa(int a)
{ return a+2; }
//------------------------
int dodaj_trzy(int a)
{ return a+3; }
//------------------------
int wywolaj(int (*wsk)(int), int liczba)
{ return (*wsk)(liczba); }

___________________________________________________________________________

10. Przeładowanie nazw funkcji

Przeładowanie nazwy funkcji polega na tym, że w danym zakresie ważności jest więcej niż
jedna funkcja o tej samej nazwie. Poszczególne funkcje różnią się typami argumentami.

Przykład

:

#include <iostream>
using namespace std;
void drukuj(int);
void drukuj(float);
void drukuj(char);
void drukuj(int, float);
void drukuj(float, int);
//-----------------
int main()
{
int a = 10;
float b = 12.6;
char c = a;

drukuj(a);

drukuj(b);

drukuj(c);

background image

Krzysztof Karbowski: Programowanie w języku C++

41

drukuj(a,b);

drukuj(b,a);

return 0;
}
//-----------------
void drukuj(int a)
{ cout << a << endl; }
//-----------------
void drukuj(float a)
{ cout << a << endl; }
//-----------------
void drukuj(char a)
{ cout << a << endl; }
//------------------
void drukuj(int a, float b)
{ cout << a << " " << b << endl; }
//------------------
void drukuj(float b, int a)
{ cout << a << " " << b << endl; }


Przykład – poniższych funkcji nie można przeładować:


void funkcja(int tab[]);
void funkcja(int *wsk);

10.1. Wska

ź

nik do funkcji przeładowanej

Przykład:

int funkcja(int);
int funkcja(float);
//------------
int (*wsk_do_fkc)(int);
wsk_do_fkc = funkcja; // do wskaznika zostanie przypisany adres

// funkcji "funkcja(int)" – wynika to z definicji wskaznika

11. Klasy

Definicja klasy:

class nazwa_klasy
{

// cialo klasy

// ..............

} ;

// <<- srednik

Definicja obiektu danej klasy (typu zdefiniowanego przez użytkownika):


nazwa_klasy zmienna;

Definicja wskaźnika i referencji do obiektu danej klasy:


nazwa_klasy * wsk ;
nazwa_klasy & refer = zmienna;

background image

Krzysztof Karbowski: Programowanie w języku C++

42

11.1. Składniki klasy

Dostęp do składników klasy:

obiekt.składnik

wskaźnik

->

składnik

referencja.składnik


Przykład

:

#include <iostream>
#include <string>
using namespace std;
//---------------------
class COsoba
{
public:

char m_sImie[80];

char m_sNazwisko[80];

int m_nWaga;

int m_nWzrost;

};
//---------------------
int main()
{
COsoba Kowalski;
COsoba &rOsoba = Kowalski; // referencja (musi byc

//

zainicjalizowana)

COsoba *pOsoba; // wskaznik

strcpy(Kowalski.m_sImie, "Jan");

strcpy(Kowalski.m_sNazwisko, "Kowalski");

Kowalski.m_nWaga = 65;

Kowalski.m_nWzrost = 178;

pOsoba = & Kowalski;


cout << Kowalski.m_sImie << " " << Kowalski.m_sNazwisko

<< endl;

cout << "waga: " << pOsoba->m_nWaga << endl;

cout << "wzrost: " << rOsoba.m_nWzrost << endl;

return 0;
}

11.2. Funkcje składowe klasy

Definicja funkcji składowej klasy jako funkcji typu

inline

:

class nazwa_klasy
{

// ............

typ nazwa_funkcji(lista_parametrow)

{

// cialo funkcji

}

};

Deklaracja i definicja funkcji składowej klasy:

class nazwa_klasy
{

background image

Krzysztof Karbowski: Programowanie w języku C++

43

// ............

typ nazwa_funkcji(lista_parametrow);

};
typ nazwa_klasy :: nazwa_funkcji(lista_parametrow)
{

// cialo funkcji

}


Przykład

:

#include <iostream>
#include <string>
using namespace std;
//---------------------
class COsoba
{
public:

char m_sImie[80];

char m_sNazwisko[80];

int m_nWaga;

int m_nWzrost;

void WstawDane(char *imie, char *nazwisko,

int waga, int wzrost);

void WydrukujDane();

};
void COsoba::WstawDane(char *imie, char *nazwisko,

int waga, int wzrost)

{

strcpy(m_sImie, imie);

strcpy(m_sNazwisko, nazwisko);

m_nWaga = waga;

m_nWzrost = wzrost;

}
void COsoba::WydrukujDane()
{

cout << m_sImie << " " << m_sNazwisko << endl;

cout << "waga: " << m_nWaga << endl;

cout << "wzrost: " << m_nWzrost << endl;

}
//---------------------
int main()
{
COsoba Kowalski;
COsoba *pOsoba;

Kowalski.WstawDane("Jan","Kowalski",65,178);

pOsoba = & Kowalski;

pOsoba->WydrukujDane();

return 0;
}

11.3. Rodzaje dost

ę

pu do składników klasy

Rodzaje dostępu do składników (zmiennych i funkcji) klasy:

private

– składniki dostępne są wyłącznie dla składników klasy,

public

– składniki są dostępne bez ograniczeń.

background image

Krzysztof Karbowski: Programowanie w języku C++

44

Przykład:

class nazwa_klasy
{
// skladniki prywatne
private:

// deklaracje/definicje składników

// ----------------------
// składniki publiczne
public:

// deklaracje/definicje składników

};

Dopóki w definicji klasy nie wystąpi żadna etykieta to składniki mają dostęp

private

.

11.4. Obiekty b

ę

d

ą

ce składnikami klasy

Przykład klasy zawierającej obiekt innej klasy:

class zarowka
{
public:

int moc;

double srednica;

void zaswiec();

void zgas();

};
class lampa
{
public:

int wysokosc;

zarowka punkt_swietlny1;

zarowka punkt_swietlny2;

void zaswiec();

void zgas();

};

11.5. Przesyłanie do funkcji argumentów b

ę

d

ą

cych obiektami

Przykład

:

#include <iostream>
#include <string>
using namespace std;
//---------------------
class COsoba
{
public:

char m_sImie[80];

char m_sNazwisko[80];

int m_nWaga;

int m_nWzrost;

void WstawDane(char *imie, char *nazwisko,

int waga, int wzrost);

};
void COsoba::WstawDane(char *imie, char *nazwisko,

int waga, int wzrost)

{

strcpy(m_sImie, imie);

strcpy(m_sNazwisko, nazwisko);

background image

Krzysztof Karbowski: Programowanie w języku C++

45

m_nWaga = waga;

m_nWzrost = wzrost;

}
//---------------------
void wydrukuj_dane(COsoba osoba);
void wydrukuj_nazwisko(COsoba & osoba);
//----------------------
int main()
{
COsoba Kowalski;

Kowalski.WstawDane("Jan","Kowalski",65,178);

wydrukuj_dane(Kowalski);

wydrukuj_nazwisko(Kowalski);

return 0;
}
//---------------------
void wydrukuj_dane(COsoba osoba)
// przekazanie obiektu przez wartosc
{

cout << osoba.m_sImie << " " << osoba.m_sNazwisko << endl;

cout << "waga: " << osoba.m_nWaga << endl;

cout << "wzrost: " << osoba.m_nWzrost << endl;

}
//----------------------
void wydrukuj_nazwisko(COsoba & osoba)
// przekazanie obiektu przez referencje
{

cout << "Nazwisko: " << osoba.m_sNazwisko << endl;

}

11.6. Składnik statyczny

class nazwa_klasy
{
// cialo klasy

static typ1 nazwa1;

// deklaracja składnika statycznego

static typ2 nazwa2;

// ...........
};
typ1 nazwa_klasy::nazwa1; // definicja składnika statycznego
typ2 nazwa_klasy::nazwa2 = wartosc; // definicja

// i inicjalizacja składnika stat.


Cechy składnika statycznego:

miejsce w pamięci dla składnika statycznego jest przydzielane tylko raz,
niezależnie od ilości obiektów danej klasy – składnik statyczny jest wspólny dla
wszystkich obiektów klasy,

składnik statyczny istnieje nawet wtedy gdy nie ma żadnego obiektu danej klasy,

dostęp do składnika statycznego:

nazwa_klasy::nazwa_skladnika

dostęp do składnika statycznego gdy istnieją obiekty danej klasy:

obiekt.nazwa_skladnika
wskaznik->nazwa_skladnika

background image

Krzysztof Karbowski: Programowanie w języku C++

46

11.7. Statyczna funkcja składowa

class nazwa_klasy
{
// ciało klasy

static typ nazwa_funkcji(parametry); // deklaracja funkcji

// ...
};

Cechy statycznej funkcji składowej:

funkcję można wywołać gdy nie istnieje żaden obiekt danej klasy,

wewnątrz funkcji nie ma wskaźnika

this

,

funkcja nie ma możliwości odwoływania się do niestatycznych składników klasy,

sposoby wywołania funkcji: takie jak dostęp do składnika statycznego.


Przykład

:

#include <iostream>
using namespace std;
class klasa
{
public:

int numer;

static long licznik;

static void zerujlicznik(){ licznik=0; }

};
long klasa::licznik = 0;
//---------
int main()
{

cout << "licznik=" << klasa::licznik << endl;

klasa obiekt1;

obiekt1.licznik++;

cout << "licznik=" << klasa::licznik << endl;

klasa obiekt2;

obiekt2.licznik++;

cout << "licznik=" << klasa::licznik << endl;

klasa::zerujlicznik();

cout << "licznik=" << klasa::licznik << endl;

return 0;
}

11.8. Funkcje składowe typu const

class nazwa_klasy
{
// ciało klasy

typ nazwa_funkcji(parametry) const;

// ............
};
typ nazwa_klasy::nazwa_funkcji(parametry) const
{

// ciało funkcji

}

Są to funkcje deklarujące, że nie będą zmieniały obiektu, na rzecz którego pracują.
Służą do pracy z obiektami zdefiniowanymi jako

const

.

background image

Krzysztof Karbowski: Programowanie w języku C++

47

12. Funkcje zaprzyja

ź

nione

Są to funkcje, które mają dostęp do składników prywatnych danej klasy.

class nazwa_klasy
{

friend typ nazwa_funkcji(parametry);

// cialo klasy
};
typ nazwa_funkcji(parametry)
{

// cialo funkcji

}

Funkcje te nie posiadają wskaźnika

this

do składników klasy, z którą są zaprzyjaźnione, a

więc muszą posługiwać się operatorami

.

(kropka) lub

->

.


Przykład

:

#include <iostream>
using namespace std;
class klasa
{

friend int przyjaciel(klasa ob);

private:

int liczba;

public:

void ustaw(int wartosc) { liczba=wartosc; }

void wyswietl() { cout << "liczba=" << liczba << endl; }

};
//---------------
int przyjaciel(klasa ob)
{

return ob.liczba;

}
//---------------
int main()
{
klasa objekt;

objekt.ustaw(10);

objekt.wyswietl();

cout << "Przyjaciel: liczba=" << przyjaciel(objekt) << endl;

return 0;
}

13. Struktury, unie i pola bitowe

13.1. Struktura

Struktura to klasa, w której wszystkie składniki są publiczne. Składnikami struktur nie mogą
być funkcje.

struct nazwa_struktury
{
// lista składnikow
};

background image

Krzysztof Karbowski: Programowanie w języku C++

48

13.2. Unia

Unia to struktura, w której poszczególne składniki zajmują to samo miejsce w pamięci.
Przykład:

union pojemnik
{

char c;

int i;

float f;

};

13.3. Pole bitowe

Pole bitowe to typ składnika klasy polegający na tym, że informacja jest przechowywana na
określonej liczbie bitów.
Przykład:

class klasa
{
public:

unsigned int bit1 : 1;

unsigned int bit4 : 4;

};

14. Konstruktory i destruktory

14.1. Konstruktor

Konstruktor to funkcja składowa klasy postaci

nazwa_klasy(parametry);

która jest automatycznie wywoływana podczas definiowania obiektu danej klasy.
Cechy konstruktora:

konstruktor może być przeładowywany,

konstruktor nie ma wyspecyfikowanego typu wartości zwracanej,

konstruktor nie zwraca żadnej wartości (nawet

void

),

konstruktor może być wywoływany dla tworzenia obiektów z modyfikatorem

const

, ale sam nie może być funkcją typu

const

,

konstruktor nie może być typu

static

,

nie można posłużyć się adresem konstruktora.

14.2. Kiedy wywoływany jest konstruktor

14.2.1. Konstruowanie obiektów lokalnych

Obiekty lokalne automatyczne – konstruktor uruchamiany jest w momencie, gdy program

napotyka definicję obiektu.

Obiekty lokalne statyczne – konstruktor zostanie uruchomiony w momencie uruchomienia

programu.

14.2.2. Konstruowanie obiektów globalnych

Konstruktor zostanie uruchomiony w momencie uruchomienia programu.

background image

Krzysztof Karbowski: Programowanie w języku C++

49

14.2.3. Konstrukcja obiektów tworzonych operatorem new

Konstruktor zostanie uruchomiony po wywołaniu operatora

new

. Konstruktor nie przydziela

miejsca w pamięci dla tworzonego obiektu – jedynie inicjalizuje wartości składników.

14.2.3. Inne przypadki uruchamiania konstruktora

Konstruktor wywoływany jest podczas tworzenia obiektów chwilowych swojej klasy.
Konstruktor jest wywoływany jeśli jest tworzony obiekt jakiejś klasy, który zawiera obiekt

klasy tego konstruktora.

14.3. Destruktor

Destruktor to funkcja składowa klasy postaci

~nazwa_klasy();

która jest automatycznie wywoływana podczas likwidowania obiektu danej klasy.
Cechy destruktora:

destruktor nie może zwracać żadnej wartości (nawet

void

),

destruktor jest wywoływany bez jakichkolwiek argumentów,

destruktor nie może być przeładowany,

nie można pobrać adresu destruktora,

destruktor nie może być funkcją typu

const

, ale może pracować na rzecz

obiektów typu

const

.


Przykład

:

#include <iostream>
#include <string>
using namespace std;
//-------------------
class napis
{
private:

char tekst[80];

char przypis[80];

int x;

int y;

public:

napis();

napis(char *t, char *p, int wspx=0, int wspy=0);

~napis();

void wyswietl();

};
//-------
napis::napis()
{

strcpy(tekst,"BRAK");

strcpy(przypis,"BRAK");

x=0; y=0;

wyswietl();

}
//--------
napis::napis(char *t, char *p, int wspx, int wspy)
{

strcpy(tekst,t);

strcpy(przypis,p);

background image

Krzysztof Karbowski: Programowanie w języku C++

50

x=wspx;

y=wspy;

wyswietl();

}
//---------
napis::~napis()
{

cout << "destruktor: " << tekst << endl;

}
//---------
void napis::wyswietl()
{

cout << "\n------------------\n";

cout << "tekst: " << tekst << endl;

cout << "przypis: " << przypis << endl;

cout << "x=" << x << " y=" << y << endl;

cout << "\n------------------\n";

}
//------------------------------------
int main()
{

napis Tekst1("Tekst1","Przypis1",10,10);

napis Tekst2;

napis Tekst3("Tekst3","Przypis3");

napis Tekst4 = napis("Tekst4","Przypis4",1,2);

napis *wsk;

wsk = new napis("Wskaznik","Przypis",5,3);

delete wsk;

return 0;
}

14.4. Konstruktor domniemany

Konstruktor domniemany ta taki konstruktor, który można wywołać bez żadnego argumentu.
Przykład:

class klasa1
{
//...........
public:

klasa1(int a);

klasa1();

// konstruktor domniemany

klasa1(float b);

//.........
};
class klasa2
{
//...........
public:

klasa2(int a);

klasa2(float b);

klasa2(int a=1; double b=3.1); // konstruktor domniemany

//............
};


Jeśli klasa nie ma żadnego konstruktora, to kompilator sam wygeneruje dla tej klasy
konstruktor domniemany.

background image

Krzysztof Karbowski: Programowanie w języku C++

51

14.5. Lista inicjalizacyjna konstruktora

Definicja konstruktora z listą inicjalizacyjną:

nazwa_klasy::nazwa_klasy(parametry) : lista_inicjalizacyjna
{

// ciało konstruktora

}

Etapy wykonania konstruktora:

1.

inicjalizacja składników z listy inicjalizacyjnej konstruktora,

2.

przypisania i inne akcje zdefiniowane w ciele konstruktora


Cechy listy inicjalizacyjnej:

składnik bez modyfikatora

const

można inicjalizować przez listę inicjalizacyjną lub

przez przypisanie w ciele konstruktora,

składnik typu

const

można inicjalizować tylko za pomocą listy inicjalizacyjnej,

lista inicjalizacyjna nie może inicjalizować składnika

static

.


Przykład

 :

#include <iostream>
#include <string>
using namespace std;
//-----------------
class klasa
{
public:

int a;

int b;

const int c;

char tekst[20];

klasa(int aa, int bb, int cc, char *t);

void wyswietl();

};
klasa::klasa(int aa, int bb, int cc, char *t)

: a(aa), b(bb), c(cc)

{

strcpy(tekst,t);

wyswietl();

}
void klasa::wyswietl()
{

cout << "---------------\n";

cout << tekst << endl;

cout << "a=" << a << " b=" << b << " c=" << c << endl;

cout << "---------------\n";

}
//---------------------
int main()
{

klasa dane(1,2,3,"Dane");

return 0;
}

background image

Krzysztof Karbowski: Programowanie w języku C++

52

14.6. Konstrukcja obiektu, którego składnikiem jest obiekt innej
klasy

Przykład

 :

#include <iostream>
#include <string>
using namespace std;

#define MAXDLSTRINGU 50

//---------------------------------
class CWyswietlacz
{
private:

int m_nMin;

int m_nMax;

int m_nWskazanie;

char m_sOpis[MAXDLSTRINGU];

public:

CWyswietlacz(const char* sOpis, int nMin, int nMax,

int nWskazanie=0);

~CWyswietlacz();

void Ustaw(int nWartosc);

void Wyswietl();

};
//------------
CWyswietlacz::CWyswietlacz(const char* sOpis, int nMin,

int nMax, int nWskazanie)

: m_nMin(nMin), m_nMax(nMax),

m_nWskazanie(nWskazanie)

{

strcpy(m_sOpis,sOpis);

cout << "Konstruktor wyswietlacza " << m_sOpis << endl;

}
//------------
CWyswietlacz::~CWyswietlacz()
{

cout << "Destruktor wyswietlacza " << m_sOpis << endl;

}
//------------
void CWyswietlacz::Ustaw(int nWartosc)
{

m_nWskazanie=nWartosc;

}
//------------
void CWyswietlacz::Wyswietl()
{

cout << m_sOpis << ": " << m_nWskazanie << endl;

}
//---------------------------------
class CPrzyrzad
{
private:

CWyswietlacz m_objWoltomierz;

CWyswietlacz m_objAmperomierz;

char m_sNazwa[MAXDLSTRINGU];

background image

Krzysztof Karbowski: Programowanie w języku C++

53

public:

CPrzyrzad(const char* sNazwa, const char* sWoltOpis,

int nWoltMin, int nWoltMax, int nWoltWskaz,

const char* sAmpOpis, int nAmpMin, int AmpMax,

int nAmpWskaz);

~CPrzyrzad();

void UstawWolt(int nWartosc);

void UstawAmp(int nWartosc);

void WyswietlWolt();

void WyswietlAmp();

};
//-----------
CPrzyrzad::CPrzyrzad(const char* sNazwa, const char* sWoltOpis,

int nWoltMin, int nWoltMax, int nWoltWskaz,

const char* sAmpOpis, int nAmpMin, int nAmpMax,

int nAmpWskaz)

: m_objWoltomierz(sWoltOpis,nWoltMin,nWoltMax,

nWoltWskaz),

m_objAmperomierz(sAmpOpis,nAmpMin,nAmpMax,nAmpWskaz)

{

strcpy(m_sNazwa,sNazwa);

cout << "Konstruktor przyrzadu " << m_sNazwa << endl;

}
//-----------
CPrzyrzad::~CPrzyrzad()
{

cout << "Destruktor przyrzadu " << m_sNazwa << endl;

}
//-----------
void CPrzyrzad::UstawWolt(int nWartosc)
{

m_objWoltomierz.Ustaw(nWartosc);

}
//-----------
void CPrzyrzad::UstawAmp(int nWartosc)
{

m_objAmperomierz.Ustaw(nWartosc);

}
//-----------
void CPrzyrzad::WyswietlWolt()
{

cout << m_sNazwa << "-> ";

m_objWoltomierz.Wyswietl();

}
//-----------
void CPrzyrzad::WyswietlAmp()
{

cout << m_sNazwa << "-> ";

m_objAmperomierz.Wyswietl();

}
//-----------------------------------------------
//-----------------------------------------------
int main()
{

CPrzyrzad objMiernik("Miernik",

"Woltomierz miernika",0,10,5,

background image

Krzysztof Karbowski: Programowanie w języku C++

54

"Amperomierz miernika",0,10,9);

objMiernik.WyswietlWolt();

objMiernik.WyswietlAmp();


CPrzyrzad objPanel("Panel",

"Woltomierz panelu",0,10,1,

"Amperomierz panelu",0,10,2);

objPanel.UstawWolt(10);

objPanel.UstawAmp(10);

objPanel.WyswietlWolt();

objPanel.WyswietlAmp();

return 0;
}


Kolejność wywołania konstruktorów:

1.

Konstruktory obiektów składowych,

2.

Konstruktor klasy zawierającej obiekty.

Kolejność wywoływania destruktorów:

1.

Destruktor klasy zawierającej obiekty.

2.

Destruktory obiektów składowych,

Obiekt klasy będącej składnikiem innej klasy może być inicjalizowany
jedynie za pomocą listy inicjalizacyjnej konstruktora.


Uwagi:

jeśli klasa obiektu składowego nie ma konstruktora, wtedy nie umiescza się go na liście
inicjalizacyjnej,

jeśli klasa obiektu składowego ma konstruktor domniemany (i ten konstruktor chcemy
użyć), to wywołanie tego konstruktora na liście inicjalizacyjnej można pominąć,

jeśli klasa obiektu składowego ma konstruktory inne niż domniemany, to pominięcie
wywołania konstruktora na liście inicjalizacyjnej spowoduje błąd kompilacji.

14.7. Konstruktor kopiuj

ą

cy (inicjalizator kopiuj

ą

cy)

Jest to konstruktor następującego typu:

klasa::klasa(klasa & obiekt);

Konstruktor kopiujący służy do skonstruowania obiektu, który jest kopią innego, już

istniejącego obiektu danej klasy. Konstruktor ten pracuje gdy następuje inicjalizacja a nie
przypisanie obiektu.

Konstruktor kopiujący można wywołać z jednym argumentem będącym referencją

obiektu danej klasy. Konstruktor ten może mieć więcej parametrów, ale muszą to być
parametry domniemane.

Przykład jawnego wywołania konstruktora kopiujacego:

K obiekt_wzor;

// definicja obiektu wzorcowego

K obiekt_nowy = K(obiekt_wzor); // definicja nowego obiektu

albo

K obiekt_nowy = obiekt_wzor;

albo

K obiekt_nowy(obiekt_wzor);


Analogicznie dla typów wbudowanych można by zapisać:

int wzor = 1;

background image

Krzysztof Karbowski: Programowanie w języku C++

55

int kopia1 = int(wzor);
int kopia2 = wzor;
int kopia3(wzor);


Niejawne wywołanie konstruktora kopiującego:

1.

podczas przesyłania obiektu do funkcji, gdy przesłanie odbywa się przez wartość,

2.

podczas zwracania obiektu jako rezultatu funkcji, jeśli funkcja zwraca rezultat przez
wartość (ta sytuacja może być zależna od implementacji kompilatora – Visual C++ 6.0
nie utworzy obiektu tymczasowego).

Przykład



#include <iostream>
#include <string>
using namespace std;
//--------------------------
class CKlasa
{
public:

int m_nLiczba;

char m_sString[30];

CKlasa(int nLiczba, const char* sString);

CKlasa(const CKlasa &objWzor);

~CKlasa();

};
CKlasa::CKlasa(int nLiczba, const char* sString)
{

m_nLiczba=nLiczba;

strcpy(m_sString,sString);

cout << "Konstruktor " << m_sString << endl;

}
CKlasa::CKlasa(const CKlasa &objWzor)
{

m_nLiczba=objWzor.m_nLiczba;

strcpy(m_sString,objWzor.m_sString);

cout << "Konstruktor kopiujacy " << m_sString << endl;

}
CKlasa::~CKlasa()
{

cout << "Destruktor " << m_sString << endl;

}
//--------------------------
void NapiszString(CKlasa objWartosc)
{

cout << "NapiszString: " << objWartosc.m_sString << endl;

}
//--------------------------
CKlasa UtworzObiekt(int nLiczba, const char* sString)
{

CKlasa objLokalny(nLiczba,sString);

return objLokalny;

}
//--------------------------
int main()
{

CKlasa objWzorzec(1,"Wzorzec");

background image

Krzysztof Karbowski: Programowanie w języku C++

56

cout << "--- Obiekt na bazie wzorca---\n";

CKlasa objKopia=objWzorzec;

// albo

// CKlasa objKopia=CKlasa(objWzorzec);

// albo

//CKlasa objKopia(objWzorzec);

cout << "---Wywolaj <<NapiszString>>---\n";

NapiszString(objWzorzec);

cout << "---Wywolaj <<UtworzObiekt>>---\n";

CKlasa objZFunkcji=UtworzObiekt(1,"Z funkcji");

cout << "----- Koniec programu ---- \n";

return 0;
}

Jeśli w klasie nie ma zdefiniowanego konstruktora kopiującego to gdy
zachodzi potrzeba wywołania takiego konstruktora, to kompilator
wygeneruje

konstruktor

kopiujący

działający

według

zasady

kopiowania "składnik po składniku".


Jeśli w programie wystarczy kopiowanie "składnik po składniku" to nie ma potrzeby
programowania

konstruktora

kopiującego

wystarczy

konstruktor

generowany

automatycznie.

14.7.1. Sytuacja, gdy niezb

ę

dny jest konstruktor kopiuj

ą

cy

Przykład



#include <iostream>
#include <string>
using namespace std;
//--------------------------
class CKlasa
{
public:

char *m_sString;

CKlasa(const char* sString);

CKlasa(const CKlasa &objWzor);

void Zmien(const char* sString);

~CKlasa();

};
CKlasa::CKlasa(const char* sString)
{

m_sString = new char[80];

strcpy(m_sString,sString);

}
CKlasa::CKlasa(const CKlasa &objWzor)
{

m_sString = new char[80];

strcpy(m_sString,objWzor.m_sString);

}
CKlasa::~CKlasa()
{

delete [] m_sString;

}
void CKlasa::Zmien(const char* sString)
{

strcpy(m_sString,sString);

background image

Krzysztof Karbowski: Programowanie w języku C++

57

}
//--------------------------
int main()
{

CKlasa objWzor("obiekt 1");

CKlasa objKopia=objWzor;

cout << "Wzor: " << objWzor.m_sString << endl;

cout << "Kopia: " << objKopia.m_sString << endl;

// zmiana tekstu

objKopia.Zmien("obiekt 2");

cout << "Po zmianie:\n";

cout << "Wzor: " << objWzor.m_sString << endl;

cout << "Kopia: " << objKopia.m_sString << endl;

return 0;
}


Na ekranie pojawi się następujący tekst:

Wzor: obiekt 1
Kopia: obiekt 1
Po zmianie:
Wzor: obiekt 1
Kopia: obiekt 2

Gdyby nie było konstruktora kopiującego pojawiłby się tekst:

Wzor: obiekt 1
Kopia: obiekt 1
Po zmianie:
Wzor: obiekt 2
Kopia: obiekt 2

15. Tablice obiektów

Agregat (skupisko danych) – to tablica obiektów danej klasy lub obiekt danej klasy, który:

1.

nie ma składników

private

i

protected

,

2.

nie ma konstruktorów,

3.

nie ma klas podstawowych,

4.

nie ma funkcji wirtualnych.

15.1 Tablice obiektów b

ę

d

ą

cych agregatami

15.1.1 Tworzenie tablic

Przykład:

class CKlasa
{
public:

int m_nA;

float m_B;

};
// .........
CKlasa aTablica[5];
for (i=0; i<5; i++)
{

aTablica[i].m_nA=1;

aTablica[i].m_B=1.3;

};

background image

Krzysztof Karbowski: Programowanie w języku C++

58

15.1.2 Inicjalizacja tablic

Przykład:

CKlasa aTab[5]=

{ 1, 1.1,

// element aTab[0]

2, 2.2,

// element aTab[1]

};

// pozostałe elementy zostan

ą

wypełnione

// zerami

15.2. Tablice obiektów nie b

ę

d

ą

cych agregatami

15.2.1. Tworzenie tablic

Jeśli z obiektów danej klasy należy utworzyć tablicę to:

klasa ta nie może mieć żadnych konstruktorów,

albo klasa ta musi mieć konstruktor domniemany,

albo definiowana tablica musi być zainicjalizowana.

15.2.2. Inicjalizacja tablic

Podczas inicjalizacji tablic należy posłużyć się konstruktorem.
Przykład

:

#include <iostream>
#include <string>
using namespace std;
//--------------------------
class CKlasa
{
public:

int m_nLiczba;

char m_sString[30];

CKlasa(int nLiczba, const char* sString);

~CKlasa();

};
CKlasa::CKlasa(int nLiczba, const char* sString)
{

m_nLiczba=nLiczba;

strcpy(m_sString,sString);

cout << "Konstruktor " << m_sString << endl;

}
CKlasa::~CKlasa()
{

cout << "Destruktor " << m_sString << endl;

}
//--------------------------
int main()
{

CKlasa aTab[3] = {

CKlasa(0,"Element0"),

CKlasa(1,"Element1"),

CKlasa(2,"Element2")

};// musza zostac zainicjalizowane WSZYSTKIE elementy

return 0;
}

background image

Krzysztof Karbowski: Programowanie w języku C++

59

15.3. Tablica obiektów definiowana operatorem new

Przykład:

class CKlasa
{
public:

int m_nA;

float m_B;

};
// .........
CKlasa *pWsk;
pWsk = new CKlasa[5];
for (i=0; i<5; i++)
{

pWsk[i].m_nA=i;

pWsk[i].m_B=0.1*i;

}
delete [] pWsk;
pWsk = NULL;


Jeśli z obiektów danej klasy należy utworzyć tablicę operatorem

new

to:

tablicy tej nie można zainicjalizować,

klasa ta nie może mieć żadnych konstruktorów,

albo klasa ta musi mieć konstruktor domniemany,

16. Konwersje

Konwersja – przekształcenie obiektu danego typu (klasy) na inny typ.

Konwersje obiektu typu A na typ B mogą być zdefiniowane przez:

1.

konstruktor klasy B przyjmujący jako jedyny argument obiekt typu A,

2.

specjalną funkcję konwertującą (operator konwersji) zdefiniowaną w klasie A.

Po zdefiniowaniu jednej z w/w funkcji konwersje będą zachodziły automatycznie (niejawnie).

16.1. Konstruktor jako konwerter


Konstruktor przyjmujący jeden argument określa konwersję od typu
tego argumentu do typu klasy, do której sam należy.


Przykład

:

#include <iostream>
#include <string>
using namespace std;
//--------------------------
class CLiczba
{
public:

double m_Liczba;

char m_sOpis[30];

CLiczba(double Liczba, const char* sOpis)

{ m_Liczba=Liczba; strcpy(m_sOpis,sOpis); }

void Napisz();

background image

Krzysztof Karbowski: Programowanie w języku C++

60

};
void CLiczba::Napisz()
{

cout << "(" << m_Liczba << ":\"" << m_sOpis << "\")";

}
//--------------------------
class CZespol
{
public:

double m_Real;

double m_Imag;

CZespol(double Real, double Imag)

: m_Real(Real), m_Imag(Imag) { }

CZespol(double Real); // konwersja z double

// zamiast powyzszego konwertera mozna zastosowac

// konstruktor: CZespol(double Real, double Imag=0);

CZespol(CLiczba objLiczba); // konwersja z CLiczba

void Napisz();

};
CZespol::CZespol(double Real)
{

m_Real=Real;

m_Imag=0;

cout << "<<Konwersja z double>>\n";

}
CZespol::CZespol(CLiczba objLiczba)
{

m_Real=objLiczba.m_Liczba;

m_Imag=0;

cout << "<<Konwersja z CLiczba>>\n";

}
void CZespol::Napisz()
{

cout << "(" << m_Real << "+" << m_Imag << "i" << ")";

}
//--------------------------
CZespol dodaj(CZespol obj1, CZespol obj2)
{

CZespol objWynik(0,0);

objWynik.m_Real = obj1.m_Real + obj2.m_Real;

objWynik.m_Imag = obj1.m_Imag + obj2.m_Imag;

return objWynik;

}
//---------------------------
int main()
{

CZespol objA(1,1), objB(2,2);

CLiczba objL(3,"trzy");

double wart=5;

CZespol objWynik(0,0);


objWynik=dodaj(objA,objB);

objA.Napisz(); cout << "+"; objB.Napisz();

cout << "="; objWynik.Napisz(); cout << endl;


objWynik=dodaj(objA,wart);

background image

Krzysztof Karbowski: Programowanie w języku C++

61

objA.Napisz(); cout << "+"<< wart;

cout << "="; objWynik.Napisz(); cout << endl;


objWynik=dodaj(objA,objL);

objA.Napisz(); cout << "+"; objL.Napisz();

cout << "="; objWynik.Napisz(); cout << endl;

return 0;
}

16.2. Funkcja konwertuj

ą

ca (operator konwersji)

Funkcja konwertująca (operator konwersji) obiektu klasy K na typ T to funkcja składowa
klasy K postaci:

K::operator T()

Cechy:

musi być funkcją składową klasy, z której dokonuje konwersji,

nie ma określonego typu zwracanego rezultatu,

ma pustą listę argumentów,

jest dziedziczona,

może być funkcją wirtualną.


Przykład

:

#include <iostream>
#include <string>
using namespace std;
//--------------------------
class CZespol
{
public:

double m_Real;

double m_Imag;

CZespol(double Real=0, double Imag=0)

: m_Real(Real), m_Imag(Imag) { }

operator double();

void Napisz();

};
CZespol::operator double()
{

cout << "<<Konwersja na double>>\n";

return m_Real;

}
void CZespol::Napisz()
{

cout << "(" << m_Real << "+" << m_Imag << "i" << ")";

}
//---------------------------
int main()
{

CZespol objA(1,1);

double wynik;

double skladnik;


objA.Napisz(); cout << endl;

skladnik=objA;

wynik = 2 + skladnik;

background image

Krzysztof Karbowski: Programowanie w języku C++

62

cout << "2 + " << skladnik << " = " << wynik << endl;

return 0;
}

16.3. Sytuacje, w których zachodzi konwersja

Niejawne konwersje zachodzą, gdy:

1.

podczas wywołania funkcji zachodzi niezgodność argumentów aktualnych z
argumentami formalnymi i jest jednoznaczna możliwość usunięcia niedopasowania
poprzez konwersję,

2.

gdy funkcja zwraca rezultat innego typu niż zadeklarowany i można jednoznacznie
przeprowadzić konwersję,

3.

w obecności operatorów (np.:

a+b

,

c-d

),

4.

podczas realizacji wyrażeń inicjalizujących,

5.

podczas realizacji wyrażeń:

if(obj)

...,

switch(obj)

...,

while(obj)

...

, for(...; obj ;...)

kompilator będzie starał się wykonać konwersję

(int)obj

.


Jawne wywołanie konwersji:

CKlasa obj;
obj = (CKlasa)obj2;

// forma rzutowania

obj = CKlasa(obj2);

// forma wywołania funkcji


17. Przeładowanie operatorów

Funkcja operatorowa:

typ_zwracany

operator

nazwa_operatora ( argumenty )

{

// ciało funkcji

}

Funkcja operatorowa może być funkcją globalną lub niestatyczną funkcją składową klasy, dla
której pracuje.

Operatory predefiniowane (kompilator sam wygeneruje funkcję operatorową klasy):

=

przypisanie

&

(jednoargumentowy)

pobranie adresu obiektu danej klasy,

new, delete

utworzenie i likwidacja obiektu danej klasy

17.1. Operatory dwuargumentowe

17.1.1. Operator dwuargumentowy jako funkcja globalna

Przykład



#include <iostream>
#include <string>
using namespace std;
//--------------------------
class CZespol
{
public:

background image

Krzysztof Karbowski: Programowanie w języku C++

63

double m_Real;

double m_Imag;

CZespol(double Real=0, double Imag=0)

: m_Real(Real), m_Imag(Imag) { }

void Napisz();

};
void CZespol::Napisz()
{

cout << "(" << m_Real << "+" << m_Imag << "i" << ")";

}
//---------------------------
CZespol operator+(CZespol arg1, CZespol arg2)
{

CZespol wynik;

wynik.m_Real = arg1.m_Real + arg2.m_Real ;

wynik.m_Imag = arg1.m_Imag + arg2.m_Imag ;

return wynik;

}
//---------------------------
int main()
{

CZespol objA(1,1);

CZespol objB(2,2);

CZespol objW;


objW=objA+objB;

// inaczej:

// objW = operator+(objA,objB);

objW.Napisz();

cout << endl ;

return 0;
}

17.1.2. Operator dwuargumentowy jako funkcja składowa klasy

Przykład

:

#include <iostream>
#include <string>
using namespace std;
//--------------------------
class CZespol
{
public:

double m_Real;

double m_Imag;

CZespol(double Real=0, double Imag=0)

: m_Real(Real), m_Imag(Imag) { }

CZespol operator+(CZespol arg2);

void Napisz();

};
void CZespol::Napisz()
{

cout << "(" << m_Real << "+" << m_Imag << "i" << ")";

}
//---------------------------
CZespol CZespol::operator+(CZespol arg2)

background image

Krzysztof Karbowski: Programowanie w języku C++

64

{

CZespol wynik;

wynik.m_Real = m_Real + arg2.m_Real ;

// inaczej:

// wynik.m_Real = this->m_Real + arg2.m_Real ;

wynik.m_Imag = m_Imag + arg2.m_Imag ;

return wynik;

}
//---------------------------
int main()
{

CZespol objA(1,1);

CZespol objB(2,2);

CZespol objW;


objW=objA+objB;

// inaczej:

// objW=objA.operator+(objB);

objW.Napisz();

cout << endl ;

return 0;
}


Funkcja operatorowa, która jest funkcją składową klasy – wymaga, aby
obiekt stojący po lewej stronie znaku operatora był obiektem jej klasy.
Operator, który jest zwykłą funkcją globalną – nie ma tego
ograniczenia.

Funckję operatorową, która pracuje na argumentach klas A i B można zrealizować jako:

1.

funkcję składową klasy A,

2.

funkcję globalną,

3.

funkcję składową klasy B.

17.2. Operatory jednoargumentowe przedrostkowe (prefiksowe)

17.2.1. Przedrostkowy operator jednoargumentowy jako funkcja globalna

Przykład

:

#include <iostream>
#include <string>
using namespace std;
//--------------------------
class CZespol
{

friend CZespol operator-(CZespol arg);

private:

double m_Real;

double m_Imag;

public:

CZespol(double Real=0, double Imag=0)

: m_Real(Real), m_Imag(Imag) { }

void Napisz();

};
void CZespol::Napisz()
{

background image

Krzysztof Karbowski: Programowanie w języku C++

65

cout << "(" << m_Real << "+" << m_Imag << "i" << ")";

}
//---------------------------
CZespol operator-(CZespol arg)
{

CZespol wynik;

wynik.m_Real = - arg.m_Real ;

wynik.m_Imag = - arg.m_Imag ;

return wynik;

}
//---------------------------
int main()
{

CZespol objA(1,1);

CZespol objW;


objW= - objA ;

// inaczej:

// objW=operator-(objA);

objW.Napisz();

cout << endl ;

return 0;
}

17.2.2. Przedrostkowy operator jednoargumentowy jako funkcja

składowa klasy

Przykład

 :

#include <iostream>
#include <string>
using namespace std;
//--------------------------
class CZespol
{
public:

double m_Real;

double m_Imag;

CZespol(double Real=0, double Imag=0)

: m_Real(Real), m_Imag(Imag) { }

CZespol operator-();

void Napisz();

};
void CZespol::Napisz()
{

cout << "(" << m_Real << "+" << m_Imag << "i" << ")";

}
CZespol CZespol::operator-()
{

CZespol wynik;

wynik.m_Real = - m_Real ;

// inaczej:

// wynik.m_Real = - (this->m_Real) ;

wynik.m_Imag = - m_Imag ;

return wynik;

}
//---------------------------

background image

Krzysztof Karbowski: Programowanie w języku C++

66

int main()
{

CZespol objA(1,1);

CZespol objW;


objW= - objA ;

// inaczej:

// objW=objA.operator-();

objW.Napisz();

cout << endl ;

return 0;
}

17.3. Operatory, które musz

ą

by

ć

niestatycznymi funkcjami

składowymi

Operatory, które muszą być niestatycznymi funkcjami składowymi:

=

[]

()

->

17.3.1. Operator przypisania =

Operator przypisania to funkcja postaci:

klasa & klasa::operator=(klasa & obiekt)

Przykład

 :

#include <iostream>
#include <string>
using namespace std;
//--------------------------
class COsoba
{
private:

char* m_sImie;

char* m_sNazw;

public:

// konstruktor

COsoba(const char* sImie, const char* sNazw);

COsoba(const COsoba& objWzor);

// konstr.kopiujacy

~ COsoba();

// destruktor

// operator przypisania

COsoba& operator=(const COsoba& objWzor);

void Napisz();

};
//===========
COsoba::COsoba(const char* sImie,const char* sNazw)
// konstruktor
{

m_sImie = new char[strlen(sImie)+1];

strcpy(m_sImie,sImie);

m_sNazw = new char[strlen(sNazw)+1];

strcpy(m_sNazw,sNazw);

cout << "Konstruktor "; Napisz();

}
//============
COsoba::COsoba(const COsoba& objWzor)
// konstr.kopiujacy
{

background image

Krzysztof Karbowski: Programowanie w języku C++

67

m_sImie = new char[strlen(objWzor.m_sImie)+1];

strcpy(m_sImie,objWzor.m_sImie);

m_sNazw = new char[strlen(objWzor.m_sNazw)+1];

strcpy(m_sNazw,objWzor.m_sNazw);

cout << "Konstruktor kopiujacy "; Napisz();

}
//============
COsoba::~COsoba()
//destruktor
{

cout << "destruktor "; Napisz();

delete [] m_sImie;

delete [] m_sNazw;

}
//=============
COsoba& COsoba::operator=(const COsoba& objWzor)
//operator przypisania
{

// destruktor

delete m_sImie;

delete m_sNazw;

// konstruktor kopiujacy

m_sImie = new char[strlen(objWzor.m_sImie)+1];

strcpy(m_sImie,objWzor.m_sImie);

m_sNazw = new char[strlen(objWzor.m_sNazw)+1];

strcpy(m_sNazw,objWzor.m_sNazw);

cout << "Operator przypisania "; Napisz();

return *this;

}
//=============
void COsoba::Napisz()
{

cout << m_sImie << " " << m_sNazw << endl;

}
//-------------------------------------
int main()
{

COsoba objJan("Jan","Kowalski");

COsoba objJan2=objJan;

COsoba objPusty("pusty","pusty");


objPusty=objJan;

//inaczej: objPusty.operator=(objJan);


objJan.Napisz();

objJan2.Napisz();

objPusty.Napisz();

return 0;
}


Sytuacje, w których operator przypisania nie jest generowany automatycznie:

1.

jeżeli klasa ma składnik const – składnik taki można tylko inicjalizować,

2.

jeżeli klasa ma składnik będący referencją – referencję można tylko inicjalizować.

background image

Krzysztof Karbowski: Programowanie w języku C++

68

17.4. Operator wysyłania/pobierania danych do/z strumienia (<<, >>)

Przykład

:

#include <iostream>
using namespace std;
//--------------------------
class CZespol
{

friend ostream& operator<<(ostream& ekran,

CZespol& objL);

friend istream& operator>>(istream& klawiatura,

CZespol& objL);

private:

double m_Real;

double m_Imag;

public:

CZespol(double Real=0, double Imag=0)

: m_Real(Real), m_Imag(Imag) { }

};
//-----------------------------
ostream& operator<<(ostream& ekran, CZespol& objL)
{

ekran << "(" << objL.m_Real << "+"

<< objL.m_Imag << "i" << ")";

return ekran;

}
//-------------------------------
istream& operator>>(istream& klawiatura, CZespol& objL)
{

cout << "\tCzesc rzeczywista: ";

klawiatura >> objL.m_Real;

cout << "\tCzesc urojona: ";

klawiatura >> objL.m_Imag;

return klawiatura;

}
//---------------------------
int main()
{

CZespol objA;


cout << "Podaj liczbe zespolona:\n";

cin >> objA;

cout << "Liczba zespolona: " << objA << endl ;

// poniewaz operator << jest lewostronnie ł

ą

czny

// mozna inaczej zapisac:

// ( ( (cout << "Liczba zespolona: ") << objA ) << endl );

// lub inaczej:

// (operator<<((cout << "Liczba zespolona: "),objA)) <<

endl;
return 0;
}

18. Dziedziczenie

Dziedziczenie polega na definiowaniu klasy, która jest odmianą innej klasy:

class CKlasaPodstawowa

background image

Krzysztof Karbowski: Programowanie w języku C++

69

{

//..............

};
class CKlasaPochodna : CKlasaPodstawowa
{

//...............

};


Przykład

:

#include <iostream>
#include <string>
using namespace std;
//------------------------------------------------------------
class COsoba
{
public:

char m_sImie[80];

char m_sNazwisko[80];

int m_nWaga;

int m_nWzrost;

COsoba(char* sImie, char* sNazwisko, int nWaga,

int nWzrost);

~COsoba();

void WypiszDane();

};
//-----------------------------------------------------------
COsoba::COsoba(char* sImie, char* sNazwisko, int nWaga,

int nWzrost)

: m_nWaga(nWaga), m_nWzrost(nWzrost)

{

strcpy(m_sImie,sImie);

strcpy(m_sNazwisko,sNazwisko);

cout << "Konstruktor obiektu :" << m_sNazwisko

<< " klasy COsoba\n";

}
//-----------------------------------------------------------
COsoba::~COsoba()
{

cout << "Destruktor obiektu :" << m_sNazwisko

<< " klasy COsoba\n";

}
//-----------------------------------------------------------
void COsoba::WypiszDane()
{

cout << m_sImie << " " << m_sNazwisko << "; " << m_nWaga <<

" kg; " << m_nWzrost << " cm\n";

}
//------------------------------------------------------------
//------------------------------------------------------------
class COsobaZAdresem : public COsoba
{
public:

char m_sUlica[80];

char m_sMiasto[80];

COsobaZAdresem(char* sImie, char* sNazwisko, int nWaga,

Lista pochodzenia

background image

Krzysztof Karbowski: Programowanie w języku C++

70

int nWzrost, char* sUlica, char* sMiasto);

~COsobaZAdresem();

void WypiszDane();

};
//------------------------------------------------------------
COsobaZAdresem::COsobaZAdresem(char* sImie, char* sNazwisko,

int nWaga, int nWzrost,

char* sUlica, char* sMiasto)

: COsoba(sImie,sNazwisko,nWaga,nWzrost)

{

strcpy(m_sUlica,sUlica);

strcpy(m_sMiasto,sMiasto);

cout << "Konstruktor obiektu :" << m_sNazwisko

<< " klasy COsobaZAdresem\n";

}
//------------------------------------------------------------
COsobaZAdresem::~COsobaZAdresem()
{

cout << "Destruktor obiektu :" << m_sNazwisko

<< " klasy COsobaZAdresem\n";

}
//------------------------------------------------------------
void COsobaZAdresem::WypiszDane()
{

cout << m_sImie << " " << m_sNazwisko << "; " << m_nWaga <<

" kg; " << m_nWzrost << " cm; ul." << m_sUlica <<

"; " << m_sMiasto << endl;

}
//-------------------------------------------------------------
//-------------------------------------------------------------

int main()
{

COsoba objKowalski("Jan","Kowalski",65,178);

objKowalski.WypiszDane();


COsobaZAdresem

objNowak("Adam","Nowak",81,180,"Dluga","Krakow");

objNowak.WypiszDane();

return 0;
}


Dziedziczenie powoduje zagnieżdżenie zakresów obiektów:

class CKlasaPodstawowa
{
public:

int x;

int y;

};
class CKlasaPochodna : public CKlasaPodstawowa
{
public:

int x;

int y;

background image

Krzysztof Karbowski: Programowanie w języku C++

71

};

W powyższym przykładzie składniki

x

i

y

klasy

CKlasaPochodna

zasłonią składniki

x

i

y

klasy

CKlasaPodstawowa

.


Zakresy ważności w klasie

COsobaZAdresem

:

COsobaZAdresem
{

char m_sImie[80];

char m_sNazwisko[80];

int m_nWaga;

int m_nWzrost;
void WypiszDane();
{

char m_sUlica[80];

char m_sMiasto[80];

void WypiszDane();

}

}

Funkcja

WypiszDane

w klasie

COsobaZAdresem

zasłoni funkcję

WypiszDane

z klasy

COsoba

. Funkcje te nie są funkcjami przeładowanymi, gdyż mają różne zakresy ważności.


W klasie pochodej można zdefiniować:

dodatkowe dane składowe,

dodatkowe funkcje składowe,

składniki, które już istnieją w klasie podstawowej.

W zdefiniowanym obiekcie klasy pochodnej będzie zawarty fragment
będący jakby obiektem klasy podstawowej, ale dziedziczenie nie polega
na tworzeniu obiektów pochodnych tylko klas pochodnych.


Dostęp do zasłoniętych składników:

COsobaZAdresem Kowalski;

//wywołanie funkcji z klasy pochodnej COsobaZAdresem

Kowalski.WypiszDane();

//wywołanie funkcji z klasy podstawowej COsoba

Kowalski.COsoba::WypiszDane();

18.1. Dost

ę

p do składników klasy podstawowej

18.1.1. Prywatne składniki klasy podstawowej


Prywatne składniki klasy podstawowej są dziedziczone, ale w zakresie klasy pochodnej oraz

z poza klasy nie ma do nich dostępu.

18.1.2. Nieprywatne składniki klasy podstawowej


Składniki

public

– są dziedziczone i dostępne w zakresie klasy pochodnej oraz z poza klasy.

Odziedziczone

po COsoba

background image

Krzysztof Karbowski: Programowanie w języku C++

72

Składniki

protected

– są dziedziczone i dostępne w zakresie klasy pochodnej ale nie są

dostępne z poza klasy.

18.1.3. Dost

ę

p do składników klasy podstawowej okre

ś

lony przez klas

ę

pochodn

ą


Rodzaje dziedziczenia:

class CklasaPodstawowa : public CKlasaPochodna
class CklasaPodstawowa : protected CKlasaPochodna
class CklasaPodstawowa : private CKlasaPochodna


Jeśli na liście dziedziczenia nie określi się jego typu to przyjęte zostanie dziedziczenie typu

private

.


Dziedziczenie typu

public

:

klasa

podstawowa

klasa

pochodna

sk

ła

d

n

ik

i

private

private

sk

ła

d

n

ik

i

protected

protected

public

public


Dziedziczenie typu

protected

:

klasa

podstawowa

klasa

pochodna

sk

ła

d

n

ik

i

private

private

sk

ła

d

n

ik

i

protected

protected

public

protected


Dziedziczenie typu

private

:

klasa

podstawowa

klasa

pochodna

sk

ła

d

n

ik

i

private

private

sk

ła

d

n

ik

i

protected

private

public

private

18.2. Wyj

ą

tki w dziedziczeniu


Nie dziedziczy się:

konstruktorów

operatora przypisania

destruktora

background image

Krzysztof Karbowski: Programowanie w języku C++

73

18.3. Kolejno

ść

wywoływania konstruktorów

Przykład



#include <iostream>
using namespace std;

class CZarowka
{
protected:

int m_nMoc;

public:

CZarowka(int nMoc);

~CZarowka();

void Zaswiec();

};
//-----------------------------------------------
CZarowka::CZarowka(int nMoc)

: m_nMoc(nMoc)

{

cout << "Konstruktor CZarowka; moc="

<< m_nMoc << endl;

}
//-----------------------------------------------
CZarowka::~CZarowka()
{

cout << "Destruktor CZarowka; moc="

<< m_nMoc << endl;

}
//-----------------------------------------------
void CZarowka::Zaswiec()
{

cout << "Swieci zarowka o mocy "

<< m_nMoc << endl;

}
//-----------------------------------------------
//-----------------------------------------------
class CStarter
{
public:

int m_nTyp;

CStarter(int nTyp);

~CStarter();

};
//-----------------------------------------------
CStarter::CStarter(int nTyp)

: m_nTyp(nTyp)

{

cout << "Konstruktor CStarter; Typ "

<< m_nTyp << endl;

}
//-----------------------------------------------
CStarter::~CStarter()
{

cout << "Destruktor CStarter; Typ "

<< m_nTyp << endl;

}

background image

Krzysztof Karbowski: Programowanie w języku C++

74

//-----------------------------------------------
//-----------------------------------------------
class CSwietlowka : public CZarowka
{
protected:

int m_nMocEkw; //ekwiwalent mocy

CStarter m_objStarter;

public:

CSwietlowka(int nMoc, int nMocEkw,

int nTypStartera);

~CSwietlowka();

void Zaswiec();

};
//-----------------------------------------------
CSwietlowka::CSwietlowka(int nMoc, int nMocEkw,

int nTypStartera)

: CZarowka(nMoc),m_objStarter(nTypStartera),

m_nMocEkw(nMocEkw)

{

cout << "Konstruktor CSwietlowka; moc="

<< m_nMoc << endl;

}
//-----------------------------------------------
CSwietlowka::~CSwietlowka()
{

cout << "Destruktor CSwietlowka; moc="

<< m_nMoc << endl;

}
//-----------------------------------------------
void CSwietlowka::Zaswiec()
{

cout << "Swieci swietlowka o mocy "

<< m_nMoc << endl;

}
//-----------------------------------------------
//-----------------------------------------------
//-----------------------------------------------
int main()
{

CSwietlowka objLampa(100,15,1);

objLampa.Zaswiec();

return 0;
}


Kolejność wywoływania konstruktorów:

1.

konstruktory klas podstawowych (starszych w hierarchii),

2.

konstruktory obiektów będących składnikami klasy pochodnej,

3.

konstruktor obiektu klasy pochodnej.

18.4. Przypisanie i inicjalizacja obiektów w warunkach dziedziczenia


Klasa pochodna nie dziedziczy operatora przypisania oraz konstruktorów (w szczególności
konstruktora kopiującego).

background image

Krzysztof Karbowski: Programowanie w języku C++

75


Gdy klasa pochodna nie definiuje swojego operatora przypisania to kompilator wygeneruje

operator przypisania pracujący metodą „składnik po składniku”. Oznacza to, że dla
typów wbudowanych zrobi ich kopie a dla typów zdefiniowanych przez użytkownika
(klas) wykorzysta operatory przypisania tych klas (o ile istnieją). Także dla klasy
podstawowej wykorzysta jej operator przypisania (jeśli go posiada) w części będącej
dziedzictwem.


Gdy klasa pochodna nie definiuje swojego konstruktora kopiującego to kompilator

wygeneruje taki konstruktor pracujący metodą „składnik po składniki) (analogicznie
jak dla operatora przypisania).

18.4.1. Definiowanie konstruktora kopiuj

ą

cego i operatora przypisania

Przykład



#include <iostream>
using namespace std;
//-----------------------------------------------
class CPrzodek
{
public:

int m_A;

CPrzodek(int A);

CPrzodek(const CPrzodek& Wzor);

~CPrzodek();

CPrzodek& operator=(const CPrzodek& Wzor);

};
//-----------------------------------------------
CPrzodek::CPrzodek(int A)

: m_A(A)

{

cout << "Konstruktor CPrzodek: "

<< m_A << endl;

}
//-----------------------------------------------
CPrzodek::CPrzodek(const CPrzodek& Wzor)
{

m_A=Wzor.m_A;

cout << "Konstruktor kopiujacy CPrzodek: "

<< m_A << endl;

}
//-----------------------------------------------
CPrzodek::~CPrzodek()
{

cout << "Destruktor CPrzodek: " << m_A << endl;

}
//-----------------------------------------------
CPrzodek& CPrzodek::operator=(const CPrzodek& Wzor)
{

m_A=Wzor.m_A;

cout << "Operator przypisania CPrzodek: "

<< m_A << endl;

return *this;

}

background image

Krzysztof Karbowski: Programowanie w języku C++

76

//-----------------------------------------------
//-----------------------------------------------
class CPotomek : public CPrzodek
{
public:

int m_B;

CPotomek(int A, int B);

CPotomek(const CPotomek& Wzor);

~CPotomek();

CPotomek& operator=(const CPotomek& Wzor);

};
//-----------------------------------------------
CPotomek::CPotomek(int A, int B)

: CPrzodek(A), m_B(B)

{

cout << "Konstruktor CPotomek: "

<< m_B << endl;

}
//-----------------------------------------------
CPotomek::CPotomek(const CPotomek& Wzor)

: CPrzodek(Wzor)

{

m_B=Wzor.m_B;

cout << "Konstruktor kopiujacy CPotomek: "

<< m_B << endl;

}
//-----------------------------------------------
CPotomek::~CPotomek()
{

cout << "Destruktor CPotomek: "

<< m_B << endl;

}
//-----------------------------------------------
CPotomek& CPotomek::operator=(const CPotomek& Wzor)
{

// wywolanie operatora przypisania klasy

// podstawowej;

// jako parametr aktualny operatora mozna podac

// "Wzor" gdyz referencja do obiektu klasy

// pochodnej moze wystapic zamiast referencji

// do obiektu klasy podstawowej

(*this).CPrzodek::operator=(Wzor);


m_B=Wzor.m_B;

cout << "Operator przypisania CPotomek: "

<< m_B << endl;

return *this;

}
//-----------------------------------------------
//-----------------------------------------------
//-----------------------------------------------
int main()
{

CPotomek obj1(1,1);

CPotomek obj2=obj1;

CPotomek obj3(3,3);

background image

Krzysztof Karbowski: Programowanie w języku C++

77


obj3=obj1;

return 0;
}

18.5. Konwersje standardowe przy dziedziczeniu


Wskaźnik do obiektu klasy pochodnej może być niejawnie przekształcony na wskaźnik

dostępnej jednoznacznie klasy podstawowej.


Referencja obiektu klasy pochodnej może być niejawnie przekształcona na referencję

jednoznacznie dostępnej klasy podstawowej.


Przykład



#include <iostream>
using namespace std;
//-----------------------------------------------
class CSamochod
{
public:

int m_Paliwo;

};
//-----------------------------------------------
class CFiat : public CSamochod
{
public:

int m_LiczbaMiejsc;

};
//-----------------------------------------------
class CCosDoJezdzenia : private CSamochod
{
public:

int m_LiczbaMiejsc;

};
//-----------------------------------------------
//-----------------------------------------------
void Zatankuj(CSamochod& Klient)
{

Klient.m_Paliwo =100;

}
//-----------------------------------------------
//-----------------------------------------------
int main()
{

CFiat objFIAT;

CCosDoJezdzenia objSAM;


// niejawna konwersja refernecji CFiat

// na CSamochod

Zatankuj(objFIAT);


// Zatankuj(objSAM); BLAD gdyz CCosDoJezdzenia

// dziedziczy CSamochod jako

// private

background image

Krzysztof Karbowski: Programowanie w języku C++

78


// jawna konwersja refernecji CCosDoJezdzenia

// na CSamochod

Zatankuj((CSamochod&)objSAM);

return 0;
}


Sytuacje, w których zachodzą konwersje standardowe:

podczas przysłania argumentów do funkcji (jako referencji lub wskaźnika).
Funkcję przyjmującą argument będący referencją obiektu klasy podstawowej można
wywołać z argumentem będącym referencją obiektu klasy pochodnej. Analogicznie
jest ze wskaźnikami.

przy zwracaniu przez funkcję rezultatu będącego referencją lub wskaźnikiem.
Funkcja zdefiniowana jako zwracająca referencję (lub wskaźnik) obiektu klasy
podstawowej może zwracać referencję (lub wskaźnik) obiektu klasy pochodnej.

przy przeładowanych operatorach.
Jeśli funkcja operatorowa spodziewa się obiektu (lub jego referencji) klasy
podstawowej, to można ją wywołać z obiektem (lub referencją)klasy pochodnej.

w wyrażeniach inicjalizujących.
Gdy ma nastąpić inicjalizacja obiektu klasy podstawowej to jako obiekt wzorcowy
można wykorzystać obiekt klasy potomnej (gdyż konstruktor kopiujący przyjmuje
referencję obiektu).

Konwersje standardowe zachodzą tylko podczas pracy z referencjami
lub wskaźnikami obiektów, a nie z samymi obiektami.

Obiekty klas pochodnych mogą być traktowane jako obiekty swych klas
podstawowych tylko podczas pracy na ich wskaźnikach lub
referencjach.

19. Funkcje wirtualne

Definiowanie funkcji wirtualnej:

class nazwa_klasy
{

// ciało klasy

virtual typ nazwa_funkcji(/* parametry */)

{

// ciało funkcji

}

};

albo

class nazwa_klasy
{

// ciało klasy

virtual typ nazwa_funkcji(/* parametry */);

};
typ nazwa_klasy::nazwa_funkcji(/* parametry */)
{

// ciało funkcji

}

background image

Krzysztof Karbowski: Programowanie w języku C++

79

Przykład



#include <iostream>
using namespace std;
//-----------------------------------------------
class CInstrument
{
public:

virtual void Zagraj()

{ cout << "Dzwiek\n"; }

};
//-----------------------------------------------
class CSkrzypce : public CInstrument
{
public:

virtual void Zagraj()

{ cout << "Dzwiek skrzypiec\n"; }

};
//-----------------------------------------------
class CFortepian : public CInstrument
{
public:

virtual void Zagraj()

{ cout << "Dzwiek fortepianu\n"; }

};
//-----------------------------------------------
void Muzyk(CInstrument& objInstr)
{

objInstr.Zagraj();

}
//-----------------------------------------------
//-----------------------------------------------
int main()
{

CInstrument objInstrument;

CSkrzypce objSkrzypce;

CFortepian objFortepian;

CInstrument* pInstrument;


cout << "Wywolanie poprzez obiekt\n";

objInstrument.Zagraj();

objSkrzypce.Zagraj();

objFortepian.Zagraj();


cout << "\nWywolanie poprzez wskaznik\n";

pInstrument = &objInstrument;

pInstrument->Zagraj();

pInstrument = &objSkrzypce;

pInstrument->Zagraj();

pInstrument = &objFortepian;

pInstrument->Zagraj();


cout << "\nWywolanie poprzez referencje\n";

Muzyk(objInstrument);

Muzyk(objSkrzypce);

Muzyk(objFortepian);

return 0;

background image

Krzysztof Karbowski: Programowanie w języku C++

80

}


Rodzaje powiązań wywołań funkcji z adresami określającymi, gdzie są te funkcje:

wczesne wiązanie – wiązanie na etapie kompilacji,
późne wiązanie – wiązanie na etapie wykonania programu.


Cechy funkcji wirtualnych:

różny zakres ważności,

te same nazwy,

te same parametry,

ten sam typ.

Sytuacje, w których dla funkcji wirtualnych zachodzi wczesne wiązanie:

wywołanie na rzecz obiektu:

obiekt.funkcja();

jawne użycie kwalifikatora zakresu:

wska

ź

nik->klasa::funkcja();

wywołanie z konstruktora/destruktora klasy podstawowej.


Klasa abstrakcyjna – klasa, która nie reprezentuje żadnego obiektu.

Funkcja czysto wirtualna – funkcja zadeklarowana w klasie abstrakcyjnej jako:

virtual typ nazwa(/* parametry *)=0;


Jeśli klasa deklaruje jedną ze swoich funkcji jako

virtual

wówczas jej destruktor

powinien zostać zadeklarowany jako

virtual

– (jest to wyjątek od reguły, że funkcje

wirtualne muszą mieć tę samą nazwę).

Przykład

 A:

Plik „Pracownik.h”

#ifndef _PRACOWNIK_H_
#define _PRACOWNIK_H_
//-----------------------------------------
class CPracownik
{
public:

char m_sImie[80];

char m_sNazwisko[80];

CPracownik(char* sImie, char* sNazwisko);

virtual void Napisz()=0;

};
//-----------------------------------------
void Wizytowka(CPracownik* Dane);
//-----------------------------------------
#endif // _PRACOWNIK_H_

background image

Krzysztof Karbowski: Programowanie w języku C++

81


Plik “Pracownik.cpp”


#include <iostream>
#include <string>

using namespace std;

#include "Pracownik.h"
//-----------------------------------------
CPracownik::CPracownik(char* sImie, char* sNazwisko)
{

strcpy(m_sImie,sImie);

strcpy(m_sNazwisko,sNazwisko);

}
//------------------------------------------

void Wizytowka(CPracownik* Dane)
{

cout << "******************************\n";

cout << "Politechnika Krakowska\n";

cout << "Katedra Inzynierii Procesow Produkcyjnych\n";

Dane->Napisz();

cout << Dane->m_sImie << " " << Dane->m_sNazwisko << endl;

cout << "******************************\n";

}


Plik „main.cpp”

#include <iostream>
using namespace std;
#include "Pracownik.h"
//-------------------------------------------
class CAsystent : public CPracownik
{
public:

CAsystent(char* sImie, char* sNazwisko)

: CPracownik(sImie,sNazwisko) {}

virtual void Napisz();

};
//---------
void CAsystent::Napisz()
{

cout << "Mgr inz.\n";

}
//-------------------------------------------
class CAdiunkt : public CPracownik
{
public:

CAdiunkt(char* sImie, char* sNazwisko)

: CPracownik(sImie,sNazwisko) {}

virtual void Napisz();

};
//---------
void CAdiunkt::Napisz()
{

cout << "Dr inz.\n";

}
//-------------------------------------------
class CProfesor : public CPracownik

background image

Krzysztof Karbowski: Programowanie w języku C++

82

{
public:

CProfesor(char* sImie, char* sNazwisko)

: CPracownik(sImie,sNazwisko) {}

virtual void Napisz();

};
//---------
void CProfesor::Napisz()
{

cout << "Prof. dr hab. inz.\n";

}
//-------------------------------------------
int main()
{

CAsystent Asystent("Jan","Nowak");

CAdiunkt Adiunkt("Adam","Kowalski");

CProfesor Profesor("Jozef","Adamski");


cout << endl;

Wizytowka(&Asystent);


cout << endl;

Wizytowka(&Adiunkt);


cout << endl;

Wizytowka(&Profesor);

return 0;
}









Wyszukiwarka

Podobne podstrony:
Języki programowania wykłady
Języki programowania wykłady
MKdI L1, Informatyka - Języki programowania
MKdI L2, Informatyka - Języki programowania
Języki programowania wykłady
Podstawy Informatyki Wykład X Object Pascal Jezyki programowania, Proste typy danych
wyklad5.cpp, JAVA jest językiem programowania obiektowego
Języki programowania zaliczenie wykłady Języki programowania3
Języki programowania zaliczenie wykłady Wykład 5
pp program wykladu zaoczne 03, wisisz, wydzial informatyki, studia zaoczne inzynierskie, podstawy pr
Program wykładu Sieci komputerowe 20010 2011 II rok STACJONARNY, Informacja naukowa i bibliotekoznaw
tp w 4 Programowanie modularne, INFORMATYKA, PROGRAMOWANIE, wykłady
Języki programowania zaliczenie wykłady Opracowanie1 2
pytania z wykładu sciaga jezyki programowania
Języki programowania zaliczenie wykłady, Opracowanie1, Języki programowania
Wykład z programowania obiektowego, Informatyka, Semsetr 2, Programowanie obiektowe

więcej podobnych podstron