Laboratorium programowania w C


Jacek Czekaj, Data ostatniej modyfikacji: 30.IX.2009.
Instytut Matematyki,
Uniwersytet ÅšlÄ…ski
Laboratorium programowania w C++
0. Literatura
[1] Brian W. Kernighan, Dennis M. Ritchie, Język ANSI C, Wydawnictwa Naukowo-Techniczne.
[2] Bjarne Stroustrup, Język C++, Wydawnictwa Naukowo-Techniczne.
1. Podstawy Þö powtórka z C
Zadanie 1.1. Opisać działanie poniższego programu.
#include
using namespace std;
int main(int argc, char** argv) {
int i;
cout << "Wpisz 0 lub 1: ";
cin >> i;
switch (i) {
case 0: cout << "Wpisano liczbe 0.";
case 1: cout << "Wpisano liczbe 1.\n";
default: cout << "Wpisano liczbe rozna od 0 i 1.\n";
}
return 0;
}
Zadanie 1.2. Opisać działanie poniższego programu.
#include
#include
using namespace std;
int main(int argc, char** argv) {
union rekord {
char kategoriaWojskowa;
char nazwiskoPanienskie[256];
} r;
r.kategoriaWojskowa =  E ;
strcpy(r.nazwiskoPanienskie, "Nowak");
cout << r.nazwiskoPanienskie << endl << r.kategoriaWojskowa;
return 0;
}
Zadanie 1.3. Opisać działanie poniższego programu.
#include
using namespace std;
int main(int argc, char** argv) {
1
const int i = 123;
const int * p;
cout << i << endl;
p = &i;
*p = 997;
cout << i << endl;
return 0;
}
Zadanie 1.4. Wykorzystując wskazniki, wypisać wartości wszystkich bajtów, które składają się na wewnętrzną
reprezentację liczby typuint. Wykonać to samo zadanie wykorzystując unie.
Przykładowe rozwiązanie:
#include
using namespace std;
int main(int argc, char** argv) {
int n;
cout << "Podaj jakas liczbe calkowita: ";
cin >> n;
union {
int n;
unsigned char c[sizeof(int)];
} u;
u.n = n;
for(int i = 0; i < sizeof(int); i ++)
cout << u.c[i] << ", ";
cout << endl;
unsigned char *c = (unsigned char *) &n;
for(i = 0; i < sizeof(int); i++)
cout << *(c+i) << ", ";
return 0;
}
Zadanie 1.5. Przeanalizować działanie poniższego programu.
#include
#define MAX_X 10
#define MAX_Y 15
using namespace std;
int main(int argc, char ** argv) {
int ** tab = new int * [MAX_Y];
tab[0] = new int [MAX_X * MAX_X];
for (int y = 1; y < MAX_Y; y ++)
tab[y] = tab[y-1] + MAX_X;
for (int y = 0; y < MAX_Y; y ++)
for (int x = 0; x < MAX_X; x ++)
tab[y][x] = x*y;
for (int y = 0; y < MAX_Y; y ++) {
for (int x = 0; x < MAX_X; x ++)
2
cout << tab[y][x] << ", ";
cout << endl;
}
delete [] tab[0];
delete [] tab;
return 0;
}
Zadanie 1.6. Napisz program, który zapyta użytkownika o imię i nazwisko, po czym napisze  Witam Panią! , albo
 Witam Pana! , w zależności od płci użytkownika.
Zadanie 1.7. Napisz program, który zapyta użytkownika o jedgo numer PESEL i numer albumu, po czym napisze
 Witam Panią! , albo  Witam Pana! , w zależności od płci użytkownika.
Zadanie 1.8. Napisać program obliczający medianę z podanych liczb.
Zadanie 1.9. Napisać program, który obliczy i wypisze iloczyn macierzy o wartościach i wymiarach wprowadzonych
przez użytkownika.
2. Strumienie wejścia/wyjścia
Zadanie 2.1. Przeanalizować działanie poniższego programu.
#include
#include
using namespace std;
int main(int argc, char **argv)
{
//TEST STRUMIENI WYJSCIOWYCH
cout << "standardowy strumien wyjsciowy znakow\n";
cerr << "standardowy niebuforowany strumien wyjsciowy komunikatow o bledach\n";
clog << "standardowy strumien wyjsciowy komunikatow o bledach\n";
wcout << "szeroki strumien odpowiadajacy cout\n";
wcerr << "szeroki strumien odpowiadajacy cerr\n";
wcerr << "szeroki strumien odpowiadajacy clog\n";
//wypisywanie liczb calkowitych
cout << "\n" << 123
<< ", " << showpos << 123;
cout.unsetf(ios_base::showpoint|ios_base::showpos);
cout << ", " << 123
<< ", " << showpos << 123;
cout.unsetf(ios_base::showpoint|ios_base::showpos);
cout << "\n" << oct << 64
<< ", " << showbase << 64 << endl;
cout << "\n" << hex << 64
<< ", " << showbase << 64
<< ", " << uppercase << 64 << endl;
cout.unsetf(ios_base::hex|ios_base::showbase|ios_base::uppercase);
//wypisywanie wartosci boole-owkich
cout << "true = " << true << ", false = " << false << endl;
cout << boolalpha << "true = " << true << ", false = " << false << endl;
cout << "true = " << true << ", false = " << false << endl;
3
//wypisywanie liczb zmiennopozycyjnych
cout << "\nformat domyslny: " << 1234.56789; //zostanie zaokraglone, a nie obciete!!!
cout.setf(ios_base::scientific,ios_base::floatfield); //maksymalnie 2 argumenty
cout << "\nformat naukowy: " << 1234.56789;
cout.setf(ios_base::scientific,ios_base::floatfield);
cout << "\njszcze raz format naukowy: " << uppercase << 1234.56789;
cout.setf(ios_base::fixed,ios_base::floatfield);
cout << "\nformat staly: " << 1234.56789;
cout.setf(ios_base::floatfield);
cout << "\nznowu format domyslny: " << 1234.56789;
cout.precision(8);
cout << "\nzmiana domyslnej precyzji (bylo 6): " << 1234.56789 << ", " << 1234.56789;
cout.precision(4);
cout << "\nkolejna zmiana: " << 1234.56789 << ", " << 1234.56789;
//pola wyjsciowe
cout << "\n";
cout.width(4);
cout << "ab";
cout.width(4);
cout << "\n";
cout << "ab"; //teraz nie bedzie spacji wiodacych!!!
cout.width(4);
cout.fill( 0 );
cout << endl << "ab";
cout.width(4);
cout.fill( 0 );
cout << endl << 12 <<  , << 13; //nie wypisze 0012000,0013 tylko 0012:13 !!!
cout.fill( 0 );
cout.width(6);
cout.setf(ios_base::left,ios_base::adjustfield);
cout << endl << -12;
cout.width(6);
cout.setf(ios_base::right,ios_base::adjustfield);
cout << endl << -12;
cout.width(6);
cout.setf(ios_base::internal,ios_base::adjustfield);
cout << endl << -12;
//wypisywanie wskaznikow
int i;
int *p = new int;
cout << "\nlokalnie: " << &i << ", pamiec wolna: " << p;
//TEST STRUMIENI WEJSCIOWYCH
char s[4];
cout << "\nPROSZE NA TERMINALU WPISAC NAPIS MAJACY WIECEJ NIZ TRZY ZNAKI\n";
cin >> s; //standardowy strumien wejsciowy znakow
cout << s << endl;
cout << "\nPROSZE ZNOWU NA TERMINALU WPISAC NAPIS MAJACY WIECEJ NIZ TRZY ZNAKI\n";
cin.width(4); //nastepne cin wczyta co najwyzej trzy znaki i doda na koniec  \0
cin >> s;
cout << s << endl;
cout << "\nPROSZE JESZCZE RAZ NA TERMINALU WPISAC NAPIS MAJACY WIECEJ NIZ TRZY ZNAKI\n";
cin.get(s, 4,  \0 );
cout << s << endl;
4
//TEST STRUMIENI PLIKOWYCH
ifstream fin ( "streams_test.cpp", ios_base::binary);
ofstream fout("Üstreams_test.cpp", ios_base::binary);
unsigned char c;
fin >> noskipws;
while (!fin.eof()) {
fin >> c;
fout << c;
}
fin.close();
fout.close();
//NA KONIEC COS, NA CO TRZEBA UWAZAC!!!
int x = 0;
cout << x << ++x << ++x << std::endl; // 221 zamiast 012
return 0;
}
Zadanie 2.2. Napisać program rysujący  choinkę o zadanej przez użytkownika wysokości. Np. choinka o wysokości
4 powinna wyglądać następująco:
*
*
***
*
***
*****
*
***
*****
*******
Przykładowe rozwiązanie:
#include
using namespace std;
int main(int argc, char** argv) {
int liczbaSegmentow;
cout << "Podaj liczbe ,,segmentow  choinki: ";
cin >> liczbaSegmentow;
for (int segment = 1; segment <= liczbaSegmentow; segment ++) {
for (int liniaSegmentu = 1; liniaSegmentu <= segment; liniaSegmentu ++) {
for (int spacja = 1; spacja <= liczbaSegmentow-liniaSegmentu; spacja ++)
cout <<   ;
for (int gwiazdka = 1; gwiazdka <= 2*liniaSegmentu-1; gwiazdka ++)
cout <<  * ;
cout << endl;
}
}
return 0;
}
Zadanie 2.3. Napisać program wypełniający  po spirali tablicę kwadratową o zadanej wielkości boku.
Przykładowe rozwiązanie:
#include
using namespace std;
int main(int argc, char** argv) {
5
int i, n, x = 0, y = 0, dir = 0, tab[100][100];
cout << "Podaj rozmiar tablicy (max 100): ";
cin >> n;
for (i = 1; i <= n*n; i ++) {
if (x+y==n-1 || (x==y && x>=n/2) || (x+1==y && xdir = (dir+1) % 4;
tab[y][x] = i;
x += dir==0?1:(dir==2?-1:0);
y += dir==1?1:(dir==3?-1:0);
}
for (y = 0; y < n; y ++) {
for (x = 0; x < n; x ++) {
cout.width(4);
cout << tab[y][x];
}
cout << endl;
}
return 0;
}
Zadanie 2.4. Wykorzystując strumieine plikowe, napisać program, który będzie przyjmował dwa argumenty ozna-
czające odpowiednio nazwę pliku wejściowego oraz nazwę pliku wyjściowego, a następnie z podanego pliku wejściowego
usunie wszystkie komentarze w stylu języka C++, a wynik zapisze do podanego pliku wyjściowego.
Przykładowe rozwiązanie:
#include
#include
#include
using namespace std;
int main(int argc, char** argv) {
ifstream fin(argv[1], ios_base::binary);
ofstream fout(argv[2], ios_base::binary);
unsigned char c, o = 0;
int state = 0;
fin >> noskipws;
while (!fin.eof()) {
fin >> c;
if (state == 0) state = 1;
else if (state == 1 && o ==  / && c ==  / ) state = 2;
else if (state == 1 && o ==  / && c ==  * ) state = 3;
else if (state == 1) fout << o;
else if (state == 2 && c ==  \n ) { state = 0; fout << c; }
else if (state == 3 && o ==  * && c ==  / ) state = 0;
o = c;
}
fin.close();
fout.close();
return 0;
}
3. Biblioteka standardowa STL
Zadanie 3.1. Przeanalizować działanie poniższego programu.
6
#include
#include
#include
#include
using namespace std;
class inicjal {
public:
inicjal(char cc): c(cc) { }
bool operator()(const string &s) const { return (s[0] == c); }
private:
const char c;
};
class nieInicjal {
public:
nieInicjal(char cc): c(cc) { }
bool operator()(const string &s) const { return (s[0] != c); }
private:
const char c;
};
inline bool specialStringsCompare(const string &s, const string &t) {
string::const_iterator is = s.begin();
string::const_iterator it = t.begin();
while (is != s.end() && it != t.end() && toupper(*is) == toupper(*it)) {
is ++;
it ++;
}
if (is == s.end())
return it != t.end();
if (it == t.end())
return false;
return toupper(*is) < toupper(*it);
}
class printElement {
public:
printElement(ostream& out) : os(out) {}
void operator() (const string &s) const { os << s << endl; }
ostream& os;
};
void printElement2(const string &s) {
cout << s << endl;
}
struct pairOfDoubles {
double x, y;
pairOfDoubles(double xx = 0, double yy = 0) : x(xx), y(yy) {}
};
int main(int argc, char** argv) {
char s1[] = "Wektor zainicjowany przy konstrukcji napisem w stylu C...";
char s2[] = "Wektor zainicjowany _po_ konstrukcji napisem w stylu C...";
int i;
vector vc1(s1, s1 + sizeof(s1) - 1); //vector vc1(s1, &s1[sizeof(s1) - 1]);
vector vc2;
7
vc2.assign(s2, s2 + sizeof(s2) - 1);
cout << "\nwektor vc1 (dostep poprzez przeciazony operator []):\n";
for (i = 0; i < vc1.size(); i ++)
cout << vc1[i];
cout << endl;
vector::iterator ivc;
cout << "\nwektor vc2 (dostep poprzez iterator):\n";
for (ivc = vc2.begin(); ivc != vc2.end(); ivc ++)
cout << *ivc;
cout << endl;
vector vs;
vs.push_back("kiwi");
vs.push_back("Mango");
vs.push_back("mango");
vs.push_back("gruszka");
vs.push_back("awokado");
vs.push_back("morela");
vs.push_back("zurawina");
cout << "\nwektor vs (dostep poprzez przeciazony operator []):\n";
for (i = 0; i < vs.size(); i ++)
cout << vs[i] << endl;
sort(vs.begin(), vs.end());
cout << "\nwektor vs (wynik po sortowaniu):\n";
for (i = 0; i < vs.size(); i ++)
cout << vs[i] << endl;
sort(vs.begin(), vs.end(), specialStringsCompare);
cout << "\nvektor vs (wynik po specjalnym sortowaniu):\n";
for (i = 0; i < vs.size(); i ++)
cout << vs[i] << endl;
sort(vs.begin(), vs.end());
vector::iterator ivs1 = find_if(vs.begin(), vs.end(), inicjal( m ));
vector::iterator ivs2 = find_if(ivs1 , vs.end(), nieInicjal( m ));
vs.erase(ivs1, ivs2);
cout << "\nwektor vs (wynik po usunieciu owocow na  m ):\n";
for (i = 0; i < vs.size(); i ++)
cout << vs[i] << endl;
list l1, l2;
l1.push_back ("kiwi");
l1.push_back ("Mango");
l1.push_back ("mango");
l1.push_front("gruszka");
l1.push_front("gruszka");
l2.push_front("mango");
l2.push_front("gruszka");
l2.push_back ("awokado");
l2.push_back ("morela");
l2.push_back ("zurawina");
8
list::iterator ils;
cout << "\nlista l1 (dostep poprzez iterator):\n";
for (ils = l1.begin(); ils != l1.end(); ils ++)
cout << *ils << endl;
cout << "\nna liscie l1 sa " << count(l1.begin(), l1.end(), "gruszka")
<< " wpisy \"gruszka\"\n";
if (find(l1.begin(), l1.end(), "kiwi") != l1.end())
cout << "\nna liscie l1 jest element \"kiwi\"\n";
else
cout << "\nna liscie l1 nie ma elementu \"kiwi\"\n";
cout << "czy na liscie l1 jest element \"kiwi\"? " << boolalpha
<< binary_search(l1.begin(), l1.end(), "kiwi") << noboolalpha << endl;
//to bedzie duzo szybsze, dla (sporej) listy posortowanej...
cout << "\nlista l2 (dostep poprzez iterator):\n";
for (ils = l2.begin(); ils != l2.end(); ils ++)
cout << *ils << endl;
l1.merge(l2);
cout << "\nlista l1 (wynik po dolaczeniu listy l2):\n";
for (ils = l1.begin(); ils != l1.end(); ils ++)
cout << *ils << endl;
cout << "\nlista l2 (wynik po dolaczeniu listy l2):\n";
for (ils = l2.begin(); ils != l2.end(); ils ++)
cout << *ils << endl;
l1.sort();
l1.unique();
cout << "\nlista l1 (wynik po wyeliminowaniu elementow zdublowanych):\n";
for (ils = l1.begin(); ils != l1.end(); ils ++)
cout << *ils << endl;
l1.sort(specialStringsCompare);
cout << "\nlista l1 (wynik po specjalnym sortowaniu)):\n";
for (ils = l1.begin(); ils != l1.end(); ils ++)
cout << *ils << endl;
l1.reverse();
cout << "\nlista l1 (wynik po odwroceniu listy):\n";
for (ils = l1.begin(); ils != l1.end(); ils ++)
cout << *ils << endl;
cout << "\nlista l1 (wypisywanie przy pomocy for_each i predykatu funkcyjnego):\n";
for_each(l1.begin(), l1.end(), printElement(cout));
//UWAGA: w funkcji for_each(,,) sa przecinki, a nie sredniki jak w instrukcji for(;;)
cout << "\nlista l1 (wypisywanie przy pomocy wskaznika do funkcji):\n";
for_each(l1.begin(), l1.end(), printElement2);
//min(class T, class T), max(class T, class T)
pairOfDoubles elem1(0.0, 1.0), elem2(2.0, 3.0);
swap(elem1, elem2);
9
cout << "\nelem1.x = " << elem1.x
<< ", elem1.x = " << elem1.y
<< ", elem2.x = " << elem2.x
<< ", elem2.y = " << elem2.y << endl;
return 0;
}
Zadanie 3.2. Wykorzystując blibliotekę STL napisać program narzędziowysort. Program powinien  tak jak
w oryginale  udostępniać opcję-nprzełączającą działanie programu w tryb sortowania numerycznego.
Przykładowe rozwiązanie:
#include
#include
#include
#include
using namespace std;
#define MAX_LINE 65536
inline bool numericStringCompare(const string &s, const string &t) {
if (s.size() == t.size())
return s < t;
else
return s.size() < t.size();
}
int main(int argc, char** argv) {
char line[MAX_LINE];
vector vp, vm;
ifstream fin(argv[2], ios_base::binary);
ofstream fout(argv[3], ios_base::binary);
fin >> noskipws;
while (!fin.eof()) {
fin.getline(line, MAX_LINE);
if (string(argv[1]) == "-n" && line[0] ==  - )
vm.push_back(string(line));
else
vp.push_back(string(line));
}
fin.close();
if (string(argv[1]) == "-n") {
sort(vm.begin(), vm.end(), numericStringCompare);
sort(vp.begin(), vp.end(), numericStringCompare);
}
else
sort(vp.begin(), vp.end());
for (int i = vm.size()-1; i >= 0 ; i --)
fout << vm[i] << endl;
for (int i = 0; i < vp.size(); i ++)
fout << vp[i] << endl;
fout.close();
return 0;
}
10
4. Przeciążanie funkcji
Zadanie 4.1. Przeanalizować działanie poniższego programu.
#include
using namespace std;
void f(double x) { cout << "f(double x)" << endl; }
void f(int i) { cout << "f(int i)" << endl; }
void f(double x, double y) { cout << "f(double x, double y)" << endl; }
void f(int i, int j) { cout << "f(int i, int j)" << endl; }
void f(double x, double y=0.0) { cout << "f(double x, double y=0.0)" << endl; }
int main(int argc, char** argv) {
f(3);
f(3.0);
f((int) 3.0);
f(3, 3);
f(3.0, 3.0);
//f(3, 3.0);
return 0;
}
5. Przeciążanie operatorów
Zadanie 5.1. Przeanalizować działanie poniższego programu.
#include
class string {
private:
char *p;
size_t size;
public:
string(const char str[]);
string(const string &str);
Üstring() { delete[] p; }
string& operator=(const string &str);
inline char& operator[](const int i) const { return p[i]; };
size_t length() { return size; };
// void toupper();
// void tolower();
friend std::ostream& operator<<(std::ostream& os, const string& str);
};
string::string(const char str[]) {
char *t = (char*) str;
while (*t !=  \0 )
t ++;
size = t - str;
p = new char[size+1];
for (int i = 0; i <= size; i ++)
p[i]=str[i];
}
11
string::string(const string &str) {
size = str.size;
p = new char[size+1];
for (int i = 0; i <= size; i ++)
p[i]=str.p[i];
}
string& string::operator=(const string &str) {
if (this != &str) {
delete [] p;
size = str.size;
p = new char[size+1];
for (int i = 0; i <= size; i ++)
p[i]=str.p[i];
}
return *this;
}
std::ostream& operator<<(std::ostream& os, const string& str) {
return os << str.p;
}
int main(int argc, char** argv) {
const char a[] = "Dzien jak c++odzien ;-)";
const char b[] = "Czy dziennik w szkole wieczorowej to nocnik?";
string s(a), t(b), u = s;
std::cout << "\n" << s
<< ", " << s[3]
<< ", " << s.length()
<< "\n" << t
<< ", " << t[3]
<< ", " << t.length()
<< "\n" << u
<< ", " << u[3]
<< ", " << u.length();
return 0;
}
Zadanie 5.2. Przeanalizować działanie poniższego programu.
#include
using namespace std;
class matrix2x2 {
private:
double m[2][2];
public:
matrix2x2(const double M[][2]);
matrix2x2(double m00 = 0.0, double m01 = 0.0, double m10 = 0.0, double m11 = 0.0);
matrix2x2(const matrix2x2 &M);
matrix2x2& operator=(const matrix2x2 &M);
matrix2x2 operator-();
double det() const;
friend matrix2x2 operator+(const matrix2x2 &M1, const matrix2x2 &M2);
friend ostream& operator<<(ostream& os, const matrix2x2 &M);
};
inline matrix2x2::matrix2x2(const double M[][2]) {
m[0][0] = M[0][0];
12
m[0][1] = M[0][1];
m[1][0] = M[1][0];
m[1][1] = M[1][1];
}
inline matrix2x2::matrix2x2(double m00, double m01, double m10, double m11) {
m[0][0] = m00;
m[0][1] = m01;
m[1][0] = m10;
m[1][1] = m11;
}
inline matrix2x2::matrix2x2(const matrix2x2 &M) {
m[0][0] = M.m[0][0];
m[0][1] = M.m[0][1];
m[1][0] = M.m[1][0];
m[1][1] = M.m[1][1];
}
inline matrix2x2& matrix2x2::operator=(const matrix2x2 &M) {
m[0][0] = M.m[0][0];
m[0][1] = M.m[0][1];
m[1][0] = M.m[1][0];
m[1][1] = M.m[1][1];
return *this;
}
inline matrix2x2 matrix2x2::operator-() {
matrix2x2 M;
M.m[0][0] = -m[0][0];
M.m[0][1] = -m[0][1];
M.m[1][0] = -m[1][0];
M.m[1][1] = -m[1][1];
return M;
}
inline double matrix2x2::det() const {
return m[0][0]*m[1][1] - m[0][1]*m[1][0];
}
inline matrix2x2 operator+(const matrix2x2 &M1, const matrix2x2 &M2) {
matrix2x2 M = M1;
M.m[0][0] += M2.m[0][0];
M.m[0][1] += M2.m[0][1];
M.m[1][0] += M2.m[1][0];
M.m[1][1] += M2.m[1][1];
return M;
}
ostream& operator<<(ostream& os, const matrix2x2 &M) {
return os << "\n" << M.m[0][0]
<< ", " << M.m[0][1]
<< "\n" << M.m[1][0]
<< ", " << M.m[1][1]
<< "\n";
}
int main(int argc, char** argv) {
const double tab[][2]= {{1.5, 2.5}, {3.5, 4.5}};
matrix2x2 A(tab), B(1.0, 0.0, 0.0, -1.0), C = A + B, D;
D = -C;
13
cout << "\nMacierz A: " << A
<< ", macierz B: " << B
<< ", macierz C: " << C
<< ", macierz D: " << D
<< ", wyznacznik macierzy A: " << A.det() //-2.0
<< ", wyznacznik macierzy B: " << B.det() //-1.0
<< ", wyznacznik macierzy C: " << C.det() // 0.0
<< ", wyznacznik macierzy D: " << D.det(); // 0.0
return 0;
}
Zadanie 5.3. Utworzyć klasÄ™matrix3x3reprezentujÄ…ca macierze kwadratowe wymiaru 3 × 3 o wyrazach rzeczy-
wistych (zob. zad. 5.2).
6. Dalej o klasach
Zadanie 6.1. Zaobserwować, na poniższym przykładzie, jakie znaczenie w definicji klasy ma słowo kluczowe
explicit.
#include
using namespace std;
class test {
private:
int private_int;
public:
explicit test(const int& = int());
test explicit_test();
test& operator++();
test& operator++(int);
friend test operator+(const test& a, const test& b);
};
test::test(const int& i) : private_int(i) { }
test test::explicit_test() { return 0; }
test& test::operator++() {
cout << "przedrostkowy\n";
++ private_int;
return *this;
}
test& test::operator++(int) {
cout << "przyrostkowy\n";
private_int ++;
return *this;
}
inline test operator+(const test& a, const test& b) {
test c;
c.private_int = a.private_int + b.private_int;
return c;
}
int main(int argc, char** argv) {
test x(1), y = test(), z = x + y, t;
z ++; ++ z;
14
return 0;
}
Zadanie 6.2. Zaobserwować, na poniższym przykładzie, jakie znaczenie w definicji klasy ma słowo kluczoweconst.
#include
#include
#include
using namespace std;
int I;
class test {
private:
int i;
public:
void f1() const;
void f2() const;
void f3();
};
//void test::f0() const { i = 1; }
void test::f1() const { }
void test::f2() const { I = 1; }
void test::f3() { i = 1; }
void f1(int i) { i = 1; }
void f2(int &i) { i = 1; }
void f3(int *i) { *i = 1; }
void f4(const int i) { i = 1; }
void f5(const int &i) { i = 1; }
void f6(const int* i) { *i = 1; } //wskaznik do stalego int-a...
void f7(int const* i) { *i = 1; } //wskaznik do stalego int-a...
void f8(int *const i) { *i = 1; } //staly wskaznik do int-a...
int main(int argc, char** argv) {
test t;
t.f1();
t.f2();
t.f3();
int k;
k = 0; f1(k); cout << k << endl;
k = 0; f2(k); cout << k << endl;
k = 0; f3(&k); cout << k << endl;
k = 0; f8(&k); cout << k << endl;
return 0;
}
7. Dziedziczenie klas
Zadanie 7.1. Przeanalizować działanie poniższego programu.
#include
using namespace std;
class abstractFigure {
public:
virtual void scale(const double s) = 0;
15
virtual double area() = 0;
virtual double length() = 0;
};
class square : public abstractFigure {
protected: //przy  private bylby blad!
double a;
public:
square(const double &A = 0) : a(A) {}
void scale(const double s) { a *= s; }
double area() { return a * a; }
double length() { return 4 * a; }
};
class rectangle : public square {
protected:
double b;
public:
rectangle(const double &A = 0, const double &B = 0) : b(B) { a = A;}
void scale(const double s) { a *= s; b *= s; }
double area() { return a * b; }
double length() { return 2*a + 2*b; }
};
//ROWNOLEGLOBOK
class parallelogram : public rectangle {
private:
double h;
public:
parallelogram(const double &A = 0, const double &B = 0, const double &H = 0)
: h(H) { a = A; b = B; }
void scale(const double s) { rectangle::scale(s); h *= s; }
//wykorzystujemy skalowanie z  rectangle
double area() { return a * h; }
//nie musimy pisac metody  length ,
//bo bylaby taka sama jak w klasie  rectangle
};
int main(int argc, char** argv) {
//figure F; //Nie mozna tworzc obiektow klas abstrakcyjnych!
//(takie klasy nie maja nawet konstruktorow)
square S(1);
rectangle R(1,2);
parallelogram P(1,2,3);
cout << "\n" << S.area()
<< ", " << S.length()
<< "\n" << R.area()
<< ", " << R.length()
<< "\n" << P.area()
<< ", " << P.length();
//cout << S.a; spowoduje blad przy kompilacji
//(bo nie ma dostepu do skladowych prywatnych i chronionych)
return 0;
}
Zadanie 7.2. Przeanalizować działanie poniższego programu.
#include
using namespace std;
16
class animal {
public:
char* name() { return "animal"; }
};
class bird : public animal {
public:
char* name() { return "bird"; }
};
class hen : public bird {
public:
char* name() { return "hen"; }
int numberOfHands() { return 0; }
int numberOfWings() { return 2; }
int numberOfLegs() { return 2; }
bool canSwim() { return false; }
};
//ssak...
class mammal : public animal {
public:
char* name() { return "mammal"; }
};
class cow : public mammal {
public:
char* name() { return "cow"; }
int numberOfHands() { return 0; }
int numberOfWings() { return 0; }
int numberOfLegs() { return 4; }
bool canSwim() { return false; }
};
class cat : public mammal {
public:
char* name() { return "cat"; }
int numberOfHands() { return 0; }
int numberOfWings() { return 0; }
int numberOfLegs() { return 4; }
bool canSwim() { return false; }
};
class dog : public mammal {
public:
char* name() { return "dog"; }
int numberOfHands() { return 0; }
int numberOfWings() { return 0; }
int numberOfLegs() { return 4; }
bool canSwim() { return true; }
};
int main(int argc, char** argv) {
animal A;
bird B;
cow C;
dog D;
hen H; // kura...
cat K;
cout << "\n" << A.name()
<< "\n" << B.name()
17
<< "\n" << C.name()
<< ", " << C.numberOfHands()
<< ", " << C.numberOfWings()
<< ", " << C.numberOfLegs()
<< ", " << C.canSwim()
<< "\n" << D.name()
<< ", " << D.numberOfHands()
<< ", " << D.numberOfWings()
<< ", " << D.numberOfLegs()
<< ", " << D.canSwim()
<< "\n" << H.name()
<< ", " << H.numberOfHands()
<< ", " << H.numberOfWings()
<< ", " << H.numberOfLegs()
<< ", " << H.canSwim()
<< "\n" << K.name()
<< ", " << K.numberOfHands()
<< ", " << K.numberOfWings()
<< ", " << K.numberOfLegs()
<< ", " << K.canSwim();
return 0;
}
Zadanie 7.3. Stosując dziedziczenie klas utworzyć klasy modelujące prostą  hierarchię pojazdów. Utworzone klasy
powinny zawierać metody zwracające nazwę pojazdu, ilość kół pojazdu, ilość drzwi pojazdu oraz rodzaj zastosowanego
napędu. Hierarchia pojazdów powinna  rozpoczynać się od pojazdu abstrakcyjnego (zob. zad. 7.2).
8. Klasy szablonowe
Zadanie 8.1. Utworzyć klasę szablonową realizującą interfejs kolejki.
Przykładowe rozwiązanie:
#ifndef QUEUE_H
#define QUEUE_H
template class queue {
public:
class queue_element {
public:
Type elem;
queue_element *next;
queue_element *prev;
};
private:
size_t queue_size;
queue_element *first, *last;
public:
explicit queue() { queue_size = 0; first = NULL; last = NULL; };
bool empty() const;
size_t size() const;
Type front() const;
Type back() const;
void push(const Type& elem);
void pop();
void clear(); //tego nie ma w interfejsie kolejki z STL-a
};
18
template bool queue::empty() const {
return (first == NULL);
}
template size_t queue::size() const {
return queue_size;
}
template Type queue::front() const {
Type elem;
if (!empty())
return first->elem;
return elem;
}
template Type queue::back() const {
Type elem;
if (!empty())
return last->elem;
return elem;
}
template void queue::push(const Type& elem) {
queue_element *temp = new queue_element;
if (temp) {
queue_size ++;
temp->elem = elem;
if (empty()) {
temp->prev = temp->next = NULL;
first = last = temp;
}
else {
temp->prev = last;
temp->next = NULL;
last->next = temp;
last = temp;
}
}
}
template void queue::pop() {
queue_element *second;
if (first != NULL) {
second = first->next;
if (second != NULL)
second->prev = NULL;
}
delete first;
queue_size --;
first = second;
}
19
template void queue::clear() {
while (!empty()) {
pop();
}
}
#endif
Zadanie 8.2. Wykorzystując utworzoną klasę szablonową kolejki napisać grę  saper .
Przykładowe rozwiązanie:
#include
#include
#include
using namespace std;
#define X 20
#define Y 10
class miner {
public:
struct pairOfInt { //to moze sie przydac w metodzie  check ...
int x, y;
pairOfInt(const int &xx = 0, const int &yy = 0) : x(xx), y(yy) {};
};
miner(const int &nn);
void check(const int &, const int &);
const bool& solved () const { return s; }
const bool& finished() const { return f; }
private:
int n, rem, board[X+2][Y+2]; //tablica z ,,zaporami  (z wartownikiem)...
bool s, f;
friend ostream& operator<<(ostream& os, const miner &M); //rysuje plansze...
};
miner::miner(const int &nn) {
int x, y;
n = nn;
rem = X*Y;
s = false;
f = false;
for (y = 1; y <= Y; y ++)
for (x = 1; x <= X; x ++)
board[x][y] = -2; //puste pole...
for (y = 0; y <= Y+1; y ++)
board[0][y] = board[X+1][y] = -3; //sciana...
for (x = 0; x <= X+1; x ++)
board[x][0] = board[x][Y+1] = -3; //sciana...
while (n) {
x = (rand() % X) + 1;
y = (rand() % Y) + 1;
if (board[x][y] == -2) {
board[x][y] = -1;
n --;
}
}
n = nn;
}
20
void miner::check(const int &x, const int &y) {
int xx, yy, ss;
pairOfInt t;
queue Q;
if (board[x][y] > 0)
return;
if (board[x][y] == -1) {
s = false;
f = true;
return;
}
Q.push(pairOfInt(x,y));
while (!Q.empty()) {
t = Q.front();
Q.pop();
ss = 0;
if (board[t.x][t.y] == -2) {
for (yy = t.y-1; yy <= t.y+1; yy ++)
for (xx = t.x-1; xx <= t.x+1; xx ++)
if ((board[xx][yy] == -1) && ((xx != t.x) || (yy != t.y)))
ss ++;
board[t.x][t.y] = ss;
if (!ss)
for (yy = t.y-1; yy <= t.y+1; yy ++)
for (xx = t.x-1; xx <= t.x+1; xx ++)
if ((board[xx][yy] == -2) && ((xx != t.x) || (yy != t.y)))
Q.push(pairOfInt(xx,yy));
rem --;
}
}
if (rem <= n)
s = f = true;
}
ostream& operator<<(ostream &os, const miner &M) {
int x, y;
char c;
for (y = 1; y <= Y; y ++) {
for (x = 1; x <= X; x ++) {
c = (M.board[x][y]>=0 ? (char)(M.board[x][y]+ 0 ) :   );
os << c;
}
os <<  | << y << endl;
}
for (x = 1; x <= X; x ++)
os <<  - ;
os <<  + << endl;
for (x = 1; x <= X; x ++) {
c = (char)(x +  a - 1);
os << c;
}
os << endl;
return os;
}
int main(int argc, char** argv) {
char c;
int i;
21
cout << "Podaj ilosc bomb: ";
cin >> i;
miner M(i);
while (!M.finished()) {
cout << M << endl << "Twoj ruch (np. e4): ";
cin >> c >> i;
M.check((int)(c- a +1), i);
}
cout << M;
if (M.solved())
cout << "Gratulacje!" << endl;
else
cout << "Buuu..." << endl;
return 0;
}
Zadanie 8.3. Przerobić klasęmatrix2x2z zadania 5.2 na klasę szablonową.
Przykładowe rozwiązanie:
#include
using namespace std;
template class matrix2x2 {
private:
type m[2][2];
public:
matrix2x2(const type M[][2]);
matrix2x2(type m00 = 0.0, type m01 = 0.0, type m10 = 0.0, type m11 = 0.0);
matrix2x2(const matrix2x2 &M);
matrix2x2& operator=(const matrix2x2 &M);
matrix2x2 operator-();
type det() const;
template friend matrix2x2 operator+
(const matrix2x2 &M1, const matrix2x2 &M2);
template friend ostream& operator<<
(ostream& os, const matrix2x2 &M);
};
template inline matrix2x2::matrix2x2(const type M[][2]) {
m[0][0] = M[0][0];
m[0][1] = M[0][1];
m[1][0] = M[1][0];
m[1][1] = M[1][1];
}
template inline matrix2x2::matrix2x2(
type m00, type m01, type m10, type m11) {
m[0][0] = m00;
m[0][1] = m01;
m[1][0] = m10;
m[1][1] = m11;
}
template inline matrix2x2::matrix2x2(const matrix2x2 &M) {
m[0][0] = M.m[0][0];
m[0][1] = M.m[0][1];
m[1][0] = M.m[1][0];
m[1][1] = M.m[1][1];
}
22
template inline matrix2x2& matrix2x2::operator=(
const matrix2x2 &M) {
m[0][0] = M.m[0][0];
m[0][1] = M.m[0][1];
m[1][0] = M.m[1][0];
m[1][1] = M.m[1][1];
return *this;
}
template inline matrix2x2 matrix2x2::operator-() {
matrix2x2 M;
M.m[0][0] = -m[0][0];
M.m[0][1] = -m[0][1];
M.m[1][0] = -m[1][0];
M.m[1][1] = -m[1][1];
return M;
}
template inline type matrix2x2::det() const {
return m[0][0]*m[1][1] - m[0][1]*m[1][0];
}
template inline matrix2x2 operator+(
const matrix2x2 &M1, const matrix2x2 &M2) {
matrix2x2 M = M1;
M.m[0][0] += M2.m[0][0];
M.m[0][1] += M2.m[0][1];
M.m[1][0] += M2.m[1][0];
M.m[1][1] += M2.m[1][1];
return M;
}
template ostream& operator<<(ostream& os, const matrix2x2 &M) {
return os << "\n" << M.m[0][0]
<< ", " << M.m[0][1]
<< "\n" << M.m[1][0]
<< ", " << M.m[1][1]
<< "\n";
}
int main(int argc, char** argv) {
const double tab[][2]= {{1.5, 2.5}, {3.5, 4.5}};
matrix2x2 A(tab), B(1.0, 0.0, 0.0, -1.0), C = A + B, D;
D = -C;
cout << "\nMacierz A: " << A
<< ", macierz B: " << B
<< ", macierz C: " << C
<< ", macierz D: " << D
<< ", wyznacznik macierzy A: " << A.det()
<< ", wyznacznik macierzy B: " << B.det()
<< ", wyznacznik macierzy C: " << C.det()
<< ", wyznacznik macierzy D: " << D.det();
return 0;
}
23
9. Zręby klas
Zadanie 9.1. Utworzyć zręb umożliwiający wygodną implementację algorytmów grafowych.
Przykładowe rozwiązanie:
#ifndef GRAPH_H
#define GRAPH_H
#include
#include
#include
using namespace std;
class vertice
{
public:
vector successors, predecessors;
size_t degree() const { return successors.size()+predecessors.size(); }
size_t outDegree() const { return successors.size(); }
size_t inDegree() const { return predecessors.size(); }
};
class graph
{
private:
size_t _numberOfEdges;
public:
vector vertices;
graph(size_t size = 0);
size_t numberOfVertices() const { return vertices.size(); }
size_t numberOfEdges() const { return _numberOfEdges; }
void insertEdge(int u, int v);
};
inline graph::graph(size_t size)
{
vertices.resize(size);
_numberOfEdges = 0;
}
inline void graph::insertEdge(int u, int v)
{
vertices[u].successors.push_back(v);
vertices[v].predecessors.push_back(u);
_numberOfEdges ++;
}
template class verticeMap
{
private:
vector _map;
public:
verticeMap(const graph& G, type value=type())
{ _map.resize(G.vertices.size(), value); }
size_t size() { return _map.size(); }
type& operator[](const int i) { return _map[i]; }
};
template class edgeMap
{
typedef vector vectorType;
24
private:
vector _map;
public:
edgeMap(const graph& G, type t=type());
size_t size() { return _map.size(); }
vectorType& operator[](const int i) { return _map[i]; }
template
friend void load(graph& G, edgeMap& c, char * filename);
};
template inline edgeMap::edgeMap(const graph& G, type value)
{
_map.resize(G.vertices.size());
for (int v = 0; v < _map.size(); v ++)
_map[v].resize(G.vertices[v].successors.size(), value);
}
template void load(graph& G, edgeMap& c, char * filename)
{
int numberOfVertices, numberOfEdges, u, v;
type t;
ifstream file(filename, ios::binary);
file >> numberOfVertices >> numberOfEdges;
G.vertices.resize(numberOfVertices);
c._map.resize(numberOfVertices);
for (v = 0; v < numberOfVertices; v ++)
{
G.vertices[v].successors.clear();
G.vertices[v].predecessors.clear();
c._map[v].clear();
}
for (int i = 0; i < numberOfEdges; i ++)
{
file >> u >> v >> t;
G.insertEdge(u, v);
c[u].push_back(t);
}
file.close();
}
#endif
Zadanie 9.2. Wykorzystać utworzony zręb do zaimplementowania programu wypisującego wszystkie ścieżki proste
w grafie.
Przykładowe rozwiązanie:
#include
#include "graph.h"
using namespace std;
struct label
{
int _v_, u;
bool onPath;
label (int _v__ = int(), int u_ = int(), bool onPath_ = bool()) :
_v_(_v__), u(u_), onPath(onPath_) { }
//friend istream& operator>>(istream& is, label &l);
};
25
int main(int argc, char ** argv)
{
graph G;
edgeMap c(G);
load(G, c, argv[1]);
const int s = 0;
const int t = G.vertices.size()-1;
verticeMap