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