10 CPA 8 Operators and enumerated types

Operatory

jednoargumentowe
binarne
potrójne


prefiksów - przed argumentowe
postfix - poargumentowe
infix - pomiędzy

Można przeładowywać operatory:

operator<<

void Stack::operator<< (int v) throw (stack_overflow) {
push(v);
}

ENUMERATED

można wykorzystać opcję preprocesora i definiować
#define SANDAY 0
             MONDAY 1...

ale lepiej:

enum weekend{SUNDAY,MONDAY...}

każdy z nich ma numer 0,1,2...
enum jest jak int

dlatego:

int day=MONDAY  czyli 1;


możemy dopisać do istniejącego ale nie tak
weekend szustnik=0;

ale:
weekend szystnik = static_cast<weekend>(0);
albo
weekend szustnik = (weekend)0;


Można też wymusić inne przypisanie wartości

enum Symbols{ALPHA=-1,BETA=1,GAMMA}

wtedy nada GAMMA następny po ostatnim podanym czyli po 1 będzie 2.

w jednej przestrzeni nazw elementy muszą być unikalne
chyba, że w różnych klasach ale wtedy i tak wywołujemy przez ::

class Animals {
public:
        enum names {DOG, CAT, CHUPACABRA};
};

class Commands {
public:
        enum names {LS, CD, CAT};
};

int main(void) {
        Animals::names a = Animals::CAT;
        Commands::names c = Commands::CAT;
        return 0;
}


żeby iterować d=d+1 trzeba przeciążyć operator +

Przeciążanie operatorów:

ZADANIA

ZAD1:












9 CPA 7 Exceptions

Wyjątki

wykrywanie problemów za pomocą warunków (przykład z funkcją gdzie wynik to arg z ref)
#include <iostream>
using namespace std;
bool div(float &res, float arg1, float arg2) {
 if(arg2 == 0.0)
  return false;
 res = arg1 / arg2;
 return true;
}
int main(void) {
 float r, a, b;
 while(cin >> a) {
  cin >> b;
  if(div(r,a,b))
   cout << r << endl;
  else
   cout << "Are you kidding me?" << endl;
 }
 return 0;
}


załączyć musimy dyrektywę
#include<exception>

dwa rodzaje błędów, logiczne i runtime

TRY włącza obserwację wyjątków (dzielenie przez 0 nie rzuca!)

catch(what) {        //łapie, ale konkretny
:
:
:
}

THROWN - rzuca konkretny ale może i rzucić string

float div(float a, float b) {
if(b == 0.0)
throw string("I can’t believe - division by zero :(");
return a / b;
}


Zeby złapać ten throw, przekazujemy przez &probl

int main(void) {
 float a, b;
 while(cin >> a) {
  try {
   cin >> b;
   cout << div(a, b) << endl;
  } catch (string &problem) {
   cout << "Look what you did, you bad user!" << endl;
   cout << problem << endl;
  }
 }
 return 0;
}


może być kilka warunków i każdy rzucać inny string do catcha

poniższy przykład zmienna str i str w catch to dwie różne zmienne
int main(void) {

string str;

try {

throw string("1");

} catch(string &str) {

cout << str;

}

return 0;

}


Jeśli Funkcja jest przerwana przez THROW to jest przerwana i nie zwraca wartości, ale obiekty stworzone w funkcji zostaną zamknięte (destruktorem) jak przy normalnym zakończeniu.

THROW może rzucić obiekt! taki obiekt nie zostanie zamknięty(zniszczony destruktorem)  po zamknieciu funkcji tylko po zakonczeniu bloku catch.

clas Class...

void DoCalculations(int i) {
if(i == 0)
throw Class("exception 1");
...

try {

   DoCalculations(i);

} catch (Class &exc) {
   cout << exc.msg << endl;
}




Funkcja MOŻE zawierać informację jakim wyjątkiem będzie rzucać:

void function(void) throw (Class) {
throw Class("object");

albo nawet kilka rodzai:
int doit(int i) throw(int, string, Class);

jeśli obiecamy jeden rodzaj a w kodzie zwróci inny to się skompiluje, ale podczas wykonywania bedzie błąd.


Jeśli funkcja rzuca różne rodzaje to możemy łapać to w różnych miejscach:
#include <iostream>
using namespace std;
class Class {
public:
 string msg;
 Class(string txt) : msg(txt) {}
};
void function(int i) throw (string,Class) {
    switch(i) {
 case 0 : throw string("string");
 case 1 : throw Class("object");
 default: cout << "OK" << endl;
    }
}
void level(int i) throw(Class) {
    try {
 function(i);
    } catch(string &exc) {
 cout << "String [" << exc << "] caught in level()" << endl;
    }    
}
int main(void) {
    for(int i = 0; i < 2; i++) {
        cout << "-------" << endl;
 try {
     level(i);
 } catch(Class &exc) {
     cout << "Object [" << exc.msg << "] caught in main()" << endl;
 }
    }
    return 0;
}



Jeśli program trafia na unexpected exception to przerywa się, ale można wywoać ostatnią funkcję. Wystarczy w main ustawić:

set_unexpected(lastchance);

i napisać funkcję lastchance np:

void lastchance(void) {
cout << "See what you've done! You've thrown an illegal exception!" << endl;
}


EXPLICIT


Jednoznaczne słowo kluczowe może zostać umieszczone przed deklaracją konstruktora klasy. Chroni konstruktor przed użyciem w dowolnym kontekście wymagającym użycia niejawnych konwersji. Ten konstruktor może być używany tylko w jawny sposób;

dlatego takie wywołanie spowoduje błąd:

class A {  
public:
   explicit A(int) {}
};

class B {  
public:
   B(int) {}
};
int main(void) {
 A a = 1;  // compilation error!
 B b = 1;
 return 0;
}

ale jak stworzymy obiekt w ten sposób, to będzie OK

A a(1);


Stworzenie takiej funkcji w przypadku zastosowania explicit też się nie powiedzie

A fun(void) { return 0; }

funkcja zwraca obiekt klasy A o wartości 0   (pytanie gdyby zwracała obiekt czy było by ok?)7.3.2



‘exception’ class

mamy klasę wyjątków zwana exception (bazowa) która ma funkcję WHAT zwracająca opis (string) wyjątku

try {
  dynamic_cast<AA &>(a).aa();
 } catch (exception ex) {
  cout << "[" << ex.what() << "]" << endl;
 }

tutaj nic nie rzuca, gdy mamy obszar kodu który się wywala możemy dać try, potem powyższy catch i opisze nam dlaczego się wywala


Klasy dziedziczące po sobie pierwsza po exception

logic_error  - logika algorytmu
domain_error   - wyjatki w zakresie liczb
invalid_argument - wyjątki niepoprawnego arguemntu do metody, funkcji, klasy
length_error - niepoprawna długośc bitowa
out_of_range - zły index do kolekcji danych
runtime_error _ różne uruchomieniowe wyjątki
range_error - obliczeniowe przekroczenie zakresu
overflow_error - za duży wynik do zmiennej
underflow_error  0 za mały wynik
bad_exception - kiedy funkcja próbuje rzucić wyjątek nieokreślony

Wszystkie powyższe można łapać za pomocą

catch(exception &ex)

należy pamiętać aby dopisać

set_unexpected(unexp);   (czy tylko w ostatnim przypadku bad exception?)

przykład kodu:
#include <iostream>
#include <exception>
using namespace std;

void unexp(void) {
 cout << "Unexpected exception arrived!" << endl;
 throw;
}

void function(void) throw(int,bad_exception) {
 throw 3.14;
}
int main(void) {
    set_unexpected(unexp);
 try {
  function();
 } catch(double f) {
  cout << "Got double" << endl;
 } catch(exception &ex) {
  cout << "It's so bad..." << endl;
 }
 cout << "Done" << endl;
 return 0;
}


równie dobrze można złapać zamiast exception &xx to konkretnie np  (bad_exception bad)



Można łapać w sposób uniwersalny:
#include <iostream>
#include <exception>
#include <stdexcept>
using namespace std;
void function(int i) {
 switch(i) {
 case 0: throw out_of_range("0");
 case 1: throw overflow_error("1");
 case 2: throw domain_error("2");
 }
}
int main(void) {
 for(int i = 0; i < 3; i++) {
  try {
   function(i);
  }
  catch(...) {
   cout << "Exception caught!" << endl;
  }
 }
 return 0;
}

ale wtedy nie rozpozna wyjątku:

albo przekazać i wyświetlic numer (przekazany w nawiasie)

catch(exception &ex) {
cout << "Exception caught: " << ex.what() << endl;
}


ale wtedy rzucony case 3 bez numeru:
case 3: throw exception();

rzuci:

Exception caught: Unknown exception


ALBO konkretnie !
int main(void) {
 for(int i = 0; i < 4; i++) {
  try {
   function(i);
  }
  catch(out_of_range &ofr) {
   cout << "Out of range: " << ofr.what() << endl;
  }
  catch(overflow_error &ovf) {
   cout << "Overflow: " << ovf.what() << endl;
  }
  catch(domain_error &dmn) {
   cout << "Domain: " << dmn.what() << endl;
  }
  catch(exception &ex) {
   cout << "Exception: " << ex.what() << endl;
  }
 }
 return 0;
}

i na koniec listy dopisać łapanie ogólne:

case 4: throw "so bad";
......
catch(...) {
cout << "Something bad happened" << endl;
}

Kolejność, od szczegółu do ogółu


Mozna zagnieżdżać Try, np try w funkcji i try w około wywołania. To czego nie wyłapie wewnątrz złapie catch zewnętrzny

A Jeśli chcemy żeby funkcja przechwyciła i potem rzuciła to samo dalej to:

catch(exception ex) {

throw ex;

}

STOS

przykład standardowego:
class Stack {
    private:
 int *stackstore;
 int stacksize;
 int SP;
    public:
 Stack(int size = 100);
 ~Stack();
 void push(int value);
 int pop(void);
};
Stack::Stack(int size) { 
 stackstore = new int[size];
 stacksize = size;
 SP = 0;
}
Stack::~Stack(void) {
 delete []stackstore;
}
void Stack::push(int value) {
 stackstore[SP++] = value;
}
int Stack::pop(void) { 
 return stackstore[--SP]; 
}

#include <iostream>
using namespace std;
int main(void) {
 Stack stk;
 stk.push(1);
 cout << stk.pop() << endl;
 return 0;
}

przykład mojego stosu w C
#include <stdio.h>
#include <stdlib.h>

typedef struct wezel {
   int val;
   struct wezel *next;
   struct wezel *head;
} wezel1;

wezel1 *head = NULL;



//dodawanie elementow stosu
void dodaj(int val) 
{
   //tworzy nowy tymczasowy,czyli nowy wezel1 o adresie temp
   //malloc alokuje pamiec o podanej wielkosci i zwraca wskaznik
   wezel1 *temp = malloc(sizeof(wezel1));

   //tymczasowy.val=val
   temp->val = val;

   //tymczasowy.next=head czyli tworzy do tylu
   temp->next = head;

   //tymczasowy staje sie nowym wezlem
   head = temp;
   printf("dodalem na stos: %d\n",val);
}


//wyswietlanie
void zdejmij() 
{

      if (head != NULL)  
      {
      wezel1 *temp = head;
      printf("Zdejmuje ze stosu :");
           
      printf(" %d\n",temp->val);
      head = temp->next;
      }
      else
      printf("Stos PUSTY, mozesz tylko dodac\n");
}


int main() {
   dodaj(5);
   dodaj(8);
   dodaj(13);
   zdejmij();
   zdejmij();
   dodaj(20);
   dodaj(80);
   zdejmij();
   zdejmij();
   zdejmij();
   zdejmij();
   dodaj(300);


   
   return 0;
}


możemy stworzyć własne wyjątki do złapania dziedziczące po takich jak chcemy:

deklaracje wyjątków:
#include <iostream>
#include <exception>
#include <stdexcept>

class stack_size_error : public std::length_error {
public:
 explicit stack_size_error(const std::string &msg);
};
class stack_bad_alloc : public std::bad_alloc {
public:
 explicit stack_bad_alloc(void);
};
class stack_overflow : public std::logic_error {
public:
 explicit stack_overflow(const std::string &msg);
};
class stack_empty : public std::logic_error {
public:
 explicit stack_empty(const std::string &msg);
};


definicje wyjatków
stack_size_error::stack_size_error(const std::string &msg) : std::length_error(msg) {
};
stack_bad_alloc::stack_bad_alloc(void) : std::bad_alloc() {
};
stack_overflow::stack_overflow(const std::string &msg) : std::logic_error(msg) {
};
stack_empty::stack_empty(const std::string &msg) : std::logic_error(msg) {
};

implementacja w klasie:
class Stack {
    private:
 int *stackstore;
 int stacksize;
 int SP;
    public:
 Stack(int size = 100) throw(stack_size_error, stack_bad_alloc);
 ~Stack();
 void push(int value) throw(stack_overflow);
 int pop(void) throw(stack_empty);
};

nowy konstruktor:
Stack::Stack(int size) throw(stack_size_error, stack_bad_alloc) { 
 if(size <= 0)
  throw stack_size_error("size must be >= 0");
 try {
  stackstore = new int[size];
 } catch(std::bad_alloc ba) {
  throw stack_bad_alloc();
 }
 stacksize = size;
 SP = 0;
}


nowa metoda push
void Stack::push(int value) throw(stack_overflow) {
 if(SP == stacksize)
  throw stack_overflow("stack size exceeded");
 stackstore[SP++] = value;
}

nowa metoda pop
int Stack::pop(void) throw(stack_empty) { 
 if(SP == 0)
  throw stack_empty("stack is empty");
 return stackstore[--SP]; 
}


ROZBICIE NA PLIKI na przykładzie stosu:

tworzymy plik nagłówkowy z deklaracjami (bez definicji)

nazwany  mystack.h

zawartość:
#ifndef __MYSTACK__
#define __MYSTACK__

#include <iostream>
#include <exception>
#include <stdexcept>

class stack_size_error : public std::length_error {
public:
 explicit stack_size_error(const std::string &msg);
};

class stack_bad_alloc : public std::bad_alloc {
public:
 explicit stack_bad_alloc(void);
};

class stack_overflow : public std::logic_error {
public:
 explicit stack_overflow(const std::string &msg);
};

class stack_empty : public std::logic_error {
public:
 explicit stack_empty(const std::string &msg);
};

class Stack {
    private:
 int *stackstore;
 int stacksize;
 int SP;
    public:
 Stack(int size = 100) throw(stack_size_error, stack_bad_alloc);
 ~Stack();
 void push(int value) throw(stack_overflow);
 int pop(void) throw(stack_empty);
};

#endif

plik z definicjami     mystack.cpp
zawartośc
#include "mystack.h"

stack_size_error::stack_size_error(const std::string &msg) : std::length_error(msg) {
};

stack_bad_alloc::stack_bad_alloc(void) : std::bad_alloc() {
};

stack_overflow::stack_overflow(const std::string &msg) : std::logic_error(msg) {
};

stack_empty::stack_empty(const std::string &msg) : std::logic_error(msg) {
};

Stack::Stack(int size) throw(stack_size_error, stack_bad_alloc) { 
 if(size <= 0)
  throw stack_size_error("size must be >= 0");
 try {
  stackstore = new int[size];
 } catch(std::bad_alloc ba) {
  throw stack_bad_alloc();
 }
 stacksize = size;
 SP = 0;
}

Stack::~Stack(void) {
 delete stackstore;
}

void Stack::push(int value) throw(stack_overflow) {
 if(SP == stacksize)
  throw stack_overflow("stack size exceeded");
 stackstore[SP++] = value;
}

int Stack::pop(void) throw(stack_empty) { 
 if(SP == 0)
  throw stack_empty("stack is empty");
 return stackstore[--SP]; 
}



plik main.cpp
#include "mystack.h"
#include <iostream>

using namespace std;

int main(void) {
 Stack stk;
 stk.push(1);
 cout << stk.pop() << endl;
 return 0;
}



kompilacja......................................................................


ZADANIA:
Zad1

#include <iostream>
    #include <exception>
 
    using namespace std;
 
    int main() {
        try {
            throw 2./4;
        }
        catch(int i) {
            cout << i;
        }
        return 0;
    }

Wynik. Błąd wykonania, rzuca zmienno przecinkowo łapie int

Zad2

#include <iostream>
    #include <exception>
    
    using namespace std;
    
    int main() {
        try {
            throw 3.14;
        }
        catch(double x) {
            x *= 2;
        }
        cout << x;
        return 0;
    }

Wynik. Błąd kompilacji. x poza catch nie istnieje


Zad3:


    #include <iostream>
    using namespace std;
    class X {
    public:
        X(void) { cout << 1; }
        ~X(void) { cout << 2; }
    };     
    X *exec() {
        X *x = new X();       //tu tworzy obiekt czyli wysw.1
        throw string("0");
        return x;
    }
 
    int main(void) {
        X *x;                   //tu nie tworzy obiektu !!!!
        try {
            delete exec();      //delete niepotrzebne, wywołanie funkcji
        } catch(string &s) {
            cout << s;           //tu łapie string "0" i wyswietla
        }
        return 0;
    }

Wynik 10


Zad4.

    #include <iostream>
    using namespace std;
    class X {
    public:
        X(void) { cout << 0; }
        ~X(void) { cout << 2; }
    };

    int main(void) {
        try {
            X *x = new X();    //tu wypisze z konstruktora 0
        throw true;
            delete x;       //tego nie wykona bo skacze do catch
        } catch(bool s) { 
            cout << s;     //łapie zmienna boolowska true czyli wypisze 1
        }
        return 0;
    }

Wynik 01. 

Zad5:



8 CPA 6 Inheritance

Dziedziczenie:

sposób zapisu:

class Sub : public Super {
};

public to pole nazwane visibility specifier, może przyjmować public, private (=brak)

jeśli
private- wszystkie publiczne klasy z superklasy zamienią się na prywatne
public - zachowa poprzednią politykę prywatności (priv=priv public=publlic)
protected - Słowo kluczowe protected oznacza, że ​​każdy oznaczony nim komponent zachowuje się                       jak składnik publiczny, gdy jest używany przez którąkolwiek z podklas i wygląda jak                           komponent prywatny na resztę świata.
                   W KLASIE POCHODNEJ POJAWI SIĘ JAKO PROTECTED (działa jak private)

Generalnie Prywatne nigdy nie przechodzą przy dziedziczeniu
Publiczne przechodzą do dziedziczonej tylko jak visibility specifier jest public
Prywatny visibility specifier (lub brak) sprawia, że publiczne zostaną prywatnymi



można dziedziczyć z 2 superklas, ale nie polecane

class Sub : public SuperA, public SuperB {

TYPY DANYCH
class Pet {
protected:
 string Name;
public:
 Pet(string n) { Name = n; }
 void Run(void) { cout << Name << ": I'm running" << endl; }
};
class Dog : public Pet {
public:
 Dog(string n) : Pet(n) {};
 void MakeSound(void) { cout << Name << ": Woof! Woof!" << endl; }
};
class Cat : public Pet {
public:
 Cat(string n) : Pet(n) {};
 void MakeSound(void) { cout << Name << ": Meow! Meow!" << endl; }
};
int main(void) {
 Pet a_pet("pet");
 Cat a_cat("Tom");
 Dog a_dog("Spike");
 a_pet.Run();
 a_dog.Run(); a_dog.MakeSound();
 a_cat.Run(); a_cat.MakeSound();
 return 0;
}

Nowa Klasa = Nowy typ danych


Objekty superklasy są kompatybilne z obiektami subklas, (góra z dołem, ale dół z górą nie)

dlatego możemy do obiektu superklasy PET przypisać obiekt subklasy DOG

a_pet = new Dog("Huckleberry");

a_pet -> Run();

Przykład

int main(void) {
 Pet *a_pet1 = new Cat("Tom");
 Pet *a_pet2 = new Dog("Spike");

 a_pet1 -> Run(); 
 // 'a_pet1 -> MakeSound();' is not allowed here!
 a_pet2 -> Run();
 // 'a_pet2 -> MakeSound();' is not allowed here!

Nie odziedziczy jednak nowych metod dopisanych do Subklasy, tylko podstawowe.

Metody subklasy (cat,dog) nie będą działać chyba, że zrzutujemy subklasę na obiekt superklasy

static_cast<Dog *>(a_pet)

taki zapis zmusza kompilator do założenia, że ​​a_pet jest (tymczasowo) przekonwertowany na wskaźnik typu Dog *.
Oznacza to, że nasz pies zyskuje zdolność szczekania
przykład:

int main(void) {
 Pet *a_pet1 = new Cat("Tom");
 Pet *a_pet2 = new Dog("Spike");
 a_pet1 -> Run(); 
 static_cast<Cat *>(a_pet1) -> MakeSound();
 a_pet2 -> Run();
 static_cast<Dog *>(a_pet2) -> MakeSound();

Odwrotnie do ob. subklasy przypisac obiekt superklasy nie działa chyba, że będziemy rzutować.


RZUTOWANIE i parametr virtual
int main(void) {
 Pet *a_pet1, *a_pet2;
 Cat *a_cat;
 Dog *a_dog;
 
 a_pet1 = a_cat = new Cat("Kitty");
 a_pet2 = a_dog = new Dog("Doggie");
 a_pet1 -> MakeSound();
 a_cat  -> MakeSound();
 a_pet2 -> MakeSound();
 a_dog  -> MakeSound();

do obiektu (wywoływanego wskaznikiem) typu PET jest przypisany obiekt subklasy typu CAT
jesli wywołamy metode MakeSound odwołując się wskaźnikiem superklasy (jak pierwsze wyw.)
to wywoła metodę z superklasy z nie z subklasy.

Możemy potrzebować wywoływać metody subklasy odwołując się do obiektu za pomocą wskaźnika superklasy (np po to by wrzucić je do Vektora). Dopisanie VIRTUAL przed metodą w superklasie zablokuje to, że będzie napisywała metodę subklasy, nawet jak przypiszemy obiekt subklasy do wskaźnika superklasy.

class Pet {
protected:
 string Name;
public:
 Pet(string n) { Name = n; }
 virtual void MakeSound(void) { cout << Name << " the Pet says: Shh! Shh!" << endl; }
};
.

wtedy cat1 zamiałczy
a_pet1 -> MakeSound();

a pet2 zaszczeka
 a_pet2 -> MakeSound();

z virtual nawet castowanie Petem na obiekt kota, nie zmieni działania klasy MakeSound.

Co ciekawe nawet jak virtualna metoda z superklasy jest wywoływana przez inną metodę (a nie bezpośrednio) to i tak wywoła metodę subklasy...

przykład WakeUp
class Pet {
protected:
 string Name;
public:
 Pet(string n) { Name = n; }
 virtual void MakeSound(void) { cout << Name << " the Pet says: Shh! Shh!" << endl; }
 void WakeUp(void) { MakeSound(); }
};
class Cat : public Pet {
public:
 Cat(string n) : Pet(n) { }
 void MakeSound(void) { cout << Name << " the Cat says: Meow! Meow!" << endl; }
};
class Dog : public Pet {
public:
 Dog(string n) : Pet(n) { }
 void MakeSound(void) { cout << Name << " the Dog says: Woof! Woof!" << endl; }
};
int main(void) {
 Pet *a_cat;
 Pet *a_dog;

 a_cat = new Cat("Kitty");
 a_cat -> WakeUp();
 a_dog = new Dog("Doggie");
 a_dog -> WakeUp();

 return 0;
}


PRZEKAZYWANIE OBIEKTOW JAKO PARAMETR FUNKCJI


Przekazywanie obiektu jako parametr (wartość) niewskazane bo tworzy kopie:

void NamePetByValue(string name, Pet pet) {
pet.NameMe(name);
}

Przekazywanie obiektu jako wskaźnik :

void NamePetByPointer(string name, Pet *pet) {
pet -> NameMe(name);                                               //zamiast kropki   ->
}


Przekazywanie obiektu jako Referencja:

void NamePetByReference(string name, Pet &pet) {
pet.NameMe(name);
}


Wywołanie dla obiektu stworzonego automatycznie czyli
PET pet;

wartość i referencja  ("imie",pet)     //  obiekt pet stworzony normalnie więc wystarczy pet
wskaźnik ("imię",&pet)                   //żeby wyciągnąc adres (wskaźnik) używamy &

Wywołanie dla obiektu zapisanego we wskaźniku i stworzonego przez new
PET *pet=new PET;

wartość i referencja("imię", *pet)   //* wyciąga wartość ze wskaźnika
wskaźnik("imię", pet);


METODY SUBKLASY jak wywołać ze wskaźnika superkalsy:

parametr funkcji jest określonego typu, np typ superklasy, jeśli chcemy przekazać subklasę, musimy nadać jej wskaźnik superklasy żeby miała jej typ.
Kiedy subklasa jest przypisana do wskaźnika superklasy i jest jej typu to:
1) przy dziedziczeniu straci obszar private dlatego nadajemy protected w superklasie
2)przy przypisaniu do wskaźnika superklasy jej wspólne metody z superklasą zostaną nadpisane
dlatego nadajemy im w superklasie VIRTUAL, wtedy nie będą
3) utraci dostęp do swoich nowych dodatkowych metod chyba że statycznie bądź dynamicznie zrzutujemy

static_cast<subklasa*>(wskaźnik do obiektu)->metoda()

Dynamicznie(wskaźnik):

GermanShepherd *gs = new GermanShepherd("Hund");

if(gs = dynamic_cast<GermanShepherd *>(pet))

gs -> Laufen();     //do wskaźnika subklasy, przypisał i dzięki temu wywołamy jej unikalne metody
                             //ze wskaźnika pet nie moglibyśmy ich wywołać  (metoda Laufen)

Dynamiczne(referencja):

GermanShepherd gs("Hund");

dynamic_cast<GermanShepherd &>(pet).Laufen();


KONSTRUKTOR KOPIUJĄCY

Podstawowa kontrukcja konstruktora kopiującego:

A(A &)


Przypisać (skopiować) jeden obiekt do drugiego można:
Class o2=o1;

albo w notacji funkcji:
Class o3(o2);

Skopiowanie obiektów bez ustawienia konstruktora kopiującego sprawi, że zmienne będą dzieliły ten sam obszar pamięci co jest niebezpieczne.

przykład
#include <iostream>
using namespace std;
class Class {
 int *data;
public:
 Class(int value) {
   data = new int;
  *data = value;
 }
 Class(Class &source) { 
   data = new int;
  *data = source.value();
 }
 void increment(void) { (*data)++; }
 int value(void) { return *data; }
};
int main(void) {
 Class o1(123);
 Class o2 = o1;
 Class o3(o2);
 o1.increment();
 cout << o1.value() << endl;
 cout << o2.value() << endl;
 cout << o3.value() << endl;
 return 0;
}



Użycie konstruktora kopiujące i przekazanie obiektu do funkcji jako parametr (przez wartość).

#include <iostream>
using namespace std;
class Dummy {
public:
 Dummy(int value) {}
 Dummy(Dummy &source) { 
  cout << "Hi from the copy constructor!" << endl;
 }
};
void DoSomething(Dummy ob) {
 cout << "I'm here!" << endl;
}
int main(void) {
 Dummy o1(123);
 DoSomething(o1);
 return 0;
}

Juz samo przekazanie do funkcji obiektu przez wartość (kopie) uruchamia jak widać konst.kop. wyświetli:
Hi from the copy constructor!     / kons. kopiujący

I'm here!                                     /efekt wywołania funkcji

wrzucenie konstruktora kopiującego do sekcji private zapobiegnie kopiowaniu obiektu (bład)


Bez konstruktora (inicjanizującego) wartości poczatkowego sprawi, że przy tworzeniu obiektu, bedą wartości całkowicie przypadkowe.

Przykład z konstruktorem i listą inicjalizacyjna:
#include <iostream>
using namespace std;
class WithConstructor {
public:
 int i;
 float f;
 WithConstructor(int a, float b) : i(a), f(b) { }
 void Display(void) { cout << "i=" << i << ",f=" << f << endl; }
};
int main(void) {
 WithConstructor  o1;
 WithConstructor *o2;
 o2 = new WithConstructor;
 o1.Display();
 o2 -> Display();
 return 0;
}

można też podać wartości domysle w przypadku nie podania argumentów

WithConstructor(int a = 0, float b = 0) : i(a), f(b) { }

ewentaulnie dla konstruktora bezparametrowego

WithConstructor(void) : i(0), f(0.0) { }

6.5.12 obiekt tworzony w innej klasie, wywołujemy wtedy obiekt1klasy.obiektstworzony.metoda()

Kontruktor dla obiektów tworzonych w klasie....(nie zrobione)

DOPISAC...

CONST

trzeba określić wartośc od razu, nie można:         const int punkty;

tablice mogą być const:

const int poinyd[5]={1,2,3,4,5};

struktury:
const struct{
int key;
} data={10};

Stała na wskaźnikach:

int * CONST iptr = 10;

SAM wskaźnik nie może być modyfikowany ++iptr nie zadziała ale wartość TAK
*iptr=0;  będzie działać


CONST  int *iptr = 100;

Wartość nie może być modyfikowana ale wskaźnik TAK.
Przed const DOTYCZY WARTOŚCI, w środku DOTYCZY WSKAŹNIKA

Można przed i w środku...


Const argumentów funkcji

int fun(const int n) {
return n * n;
}

Tak czy inaczej argument przez wartość, nie podlega zmianie, jeśli dasz const na argument przekazywany przez referencję, nie będzie działać (wiadomo, że referencja ulegnie zmianie)

int fun(const int &n) {
return n++;
}


Wartość zwracana przez funkcję jako stała:

const char *fun(void) {
return "Caution!";
}

(bez sensu dla int czy float ale dobre dla wskaźników czy obiektów)

zadziała tylko kiedy wynik funkcji przypisany do stałej:


const char *str = fun();


Zmienne w klasie jako stałe:
mogą być, ale ich wartość KONIECZNIE musi być zainicjowana na liście inicjalizacyjnej w konstruktorze:

class Class {
private:
 const int field;
public:
 Class(int n) : field(n) { };
 Class(Class &c) : field(0) { };
 Class(void) : field(1) { };
};

konstruktor nie może inicjować w klasyczny sposób:

Class(int f) { field = f; }



Jeśli stworzymy obiekt jako CONST

const Class o2(2);

To nie pozwoli nawet uruchomić metody która nic nie zmienia.

Jeśli do powyższej klasy dodamy dwie metody i jedna będzie miała const to ta metoda nie będzie w stanie zmienić niczego w naszej klasie, czego będzie pilnował kompilator:

void set(int n) { field = n; }
int get(void) const { return field; }

set może zmieniać naszą klasę, próba zmiany czegokolwiek przez get spowoduje błąd, patrz na umiejscowienie const w definicji metody (w deklaracji było by tak: type name(parameters) const;




KLASY ZAPRZYJAŹNIONE:

Zaprzyjaźnione funkcje i klasy mogą korzystać z prywatnych zmiennych i metod. Friend się nie dziedziczy.


przykład zaprzyjaźnionej klasy i tego jak może ona uzyskać dostęp do prywatnych pól innej klasy:

#include <iostream>
using namespace std;
class Class {
friend class Friend;
private:
 int field;
 void print(void) { cout << "It's a secret, that field = " << field << endl; }
};
class Friend {
public:
 void DoIt(Class &c) { c.field = 100; c.print(); }
};
int main(void) {
 Class o;
 Friend f;
 
 f.DoIt(o);
 return 0;
}

klasa może mieć wiele firns
klasa moze byc friends wielu innych klas
przyjaciel mojego przyjaciela nie jest moim przyjacielem, tylko konkretne deklaracje

class A {
friend class B;                      /przyjazn z całą klasą B
friend void C::dec(A&);      /przyjazn z metoda klasy C
friend void DoIt(A&);         /okresla przyjazn z samodzielna funkcja
private:
int field;

...
void DoIt(A &a) {                /samodzielna funkcja
a.field = 99;


ZADANIA:

Zad1:

    #include <iostream>
    using namespace std;
 
    class X {
    protected:
        int v;
    };
 
    class Y : protected X {
        Y() : v(0) {}
    }
 
    int main() {
        Y *y = new Y();
        cout << y->v;
        delete y;
        return 0;
    }

Wynik: Błąd kompilacji, bo Y dziedziczy z visibility specifier protected, czyli nie ma zmiennej v


Zad2:

#include <iostream>
    using namespace std;
    
    class X { };
    class Y : public X { };
    class Z : public X { };
    
    int main() {
        Z *z = new Z();
        Y *y = new Y();
        z = y;
        cout << (z == y);
        return 0;
    }

Wynik: Błąd kompilacji, bo z i y są różnego TYPU, musiały by być oba typu X 


Zad3:

    #include <iostream>
    using namespace std;
    
    class X { };
    class Y : public X { };
    class Z : public X { };
    
    int main() {
        Z *z = new Z();
        X *x = new X();
        x = z;
        cout << (x == z);
        return 0;
    }

Wynik: 1. Podobne jak wyżej, ale mozna przypisać bo x jest obiektem superklasy a ona jest kompatybilna z obiektami subklas (odwrotnie już nie!!!)


Zad4:

    #include <iostream>
    using namespace std;
    
    class X { 
    public:
        void shout() { cout << "X"; } 
    };
    class Y : public X { 
    };
    class Z : public Y { 
    public:
        void shout() { cout << "Z"; }
    };
    
    int main() {
        Z *z = new Z();
        static_cast<Y *>(z) -> shout();
        return 0;
    }

Wynik X. Ciekawy przypadek, bo na obiekt z (który ma nadpisaną metodę) rzutowana jest inna subklasa tej samej superklasy która nie ma nadpisanej tej metody, ale mimo to, rzutowanie sprawiło, że wraca metoda oryginalnej SUPERKLASY czyli zachowa się jak obiekt klasy która rzutowała.
chyba, że damy virtual


Zad5
#include <iostream>
    using namespace std;
    
    class X { 
    public:
        void shout() { cout << "X"; } 
    };
    class Y : public X { 
    public:
        virtual void shout() { cout << "Y"; } 
    };
    class Z : public Y { 
    public:
        void shout() { cout << "Z"; }
    };
    
    int main() {
        Y *y = new Z();
        dynamic_cast<X *>(y) -> shout();
        return 0;
    }

Wynik X. Połączenie parametru virtual, tak czy inaczej zachowa się jak obiekt klasy która rzutowała
bo x nie ma virtual

Zad6:

#include <iostream>
    using namespace std;
    
    class A { 
    public:
        A() : val(0) {}
        int val;
        void inc() { ++val; } 
    };
    
    void Do(A a) {
        a.inc();
    }
    
    int main() {
        A a;
        Do(a);
        a.inc();
        cout << a.val;
        return 0;
    }

Wynik 1. Podchwytliwe, bo funkcja operuje ka wartości czyli kopii i nie zwiększa obiektu. samo wywołanie a.inc() zwiększa do jednego a w cout następuje tylko wyświeltnie.

Zad7:

    #include <iostream>
    using namespace std;
    
    class A { 
    public:
        A() : val(0) {}
        int val;
        void run() { cout << val; } 
    };
    
    class B : public A {
    public:
        virtual void run() { cout << val + 2; } 
    };
    
    class C : public B {
    };
    
    void Do(A *a) {
        B *b;
        C *c;
        if(b = static_cast<B *>(a))
            b->run();
        if(c = dynamic_cast<C *>(b))
            c->run();
        a->run();
    }
    
    int main() {
        A *a = new C();
        Do(a);
        return 0;
    }

Wynik 220. pierwsza 2, rzutujemy klasę, B bo za pomocą wskaźnika typu B chcemy uruchomić metodę run, ale sama klasa dostarczona do funkcji pomimo, że jest typu A, to new C() (która dziedziczy po B, czyli wywołanie metody run uruchomi metodę B a da dodaje 2
                    druga 2 działa tak samo, rzutujemy C tylko po to by posłużyć się wskaźnikiem C do wywołania metody ale sam obiekt chociaż typu A jest newC i dziedziczy po B.

Zad8:

    #include <iostream>
    using namespace std;
    
    class A { 
        int *val;
    public:
        A() { val = new int; *val = 0; }
        int get() { ++(*val); return *val; } 
    };
    
    int main() {
        A a,b = a;
        cout << a.get();
        cout << b.get();
        return 0;
    }
Wynik 12 . obiekt b=a  wywołanie pierwszego a.get zwieksza do 1 i go wyswietla, wywołanie b.get zwieksza do 2 i go wyswietla, brak konstruktora kopiujacego

zad9:

#include <iostream>
    using namespace std;

    class A {
        int *val;
    public:
        A() { val = new int; *val = 0; }
        A(A &a) { val = new int; *val = a.get(); }
        int get() { ++(*val); return *val; }
    };

    int main() {
        A a,b = a;
        cout << a.get() << b.get();
        return 0;
    }

Wynik 22: podobne do poprzedniego, ale jest konstruktor kopiujący, który owszem tworzy osobną kopię, ale jednocześnie wywołuje get obiektu a więc on już ma wartość 1 , tak jak i b. Wiec kiedy wywołujemy do wyświetlenia dla nich get, zwieksza się (tym razem osobno) jeden i drugi i wyświetla 2  2.

Zad10:
    #include <iostream>
    using namespace std;
    
    class A { 
    friend class B;
    private:
        int field;
    public:
        int set(int x) { return field = ++x; }
        int get() { return ++field; }
    };
    
    class B {
    public:
        void kill(A &a) { a.field = 0; }
    };
    
    int main() {
        A a; B b;
        a.set(1);
        b.kill(a);
        cout << a.get();
        return 0;
    }

Wynik 1:  klasa B zaprzyjazniona, ma dostęp do pola field i go zeruje, ale uzycie get zwieksza do jeden wiec wyswietla 1.