Dr.Paneas Δημοσιεύτηκε Ιούλιος 30, 2008 #1 Κοινοποίηση Δημοσιεύτηκε Ιούλιος 30, 2008 Λοιπον τελικα στις διακοπες δεν ειχα ιντερνετ οπως συμφωνησαμε. Αλλα ... Αλλα ... Γραμμένο απο εμένα. Οι κώδικες εχουν γινει compile με g++ . Καλή ανάγνωση. Α ν τ ι κ ε ι μ ε ν ο σ τ ρ ε φ η ς ## ################## π ρ ο γ ρ α μ μ α τ ι σ μ ο ς ## #################################### σ ε γ λ ω σ σ α C + + ## ## Καποια βασικά για να πιάσετε την ιδέα: κλασσική αναλογία για να κατανοήσετε τον OOp είναι αυτή με το αμάξι. Υποθέστε λοιπόν ότι οδηγάτε το αυτοκίνητο σας και θέλετε να πάτε πιο γρήγορα. Γι αυτό πατάτε περισσότερο το γκάζι. Τόσο απλό. Τι πρέπει να προηγηθεί όμως πριν μπορέσετε να το κάνετε αυτό ; 1) Πρέπει να κατασκευαστεί το αυτοκίνητο. 2) Για να κατασκευαστεί το αυτοκίνητο πρέπει να υπάρχουν μηχανολογικά σχέδια αυτού. 3) Αυτά τα σχέδια, περιλαμβάνουν τα σχέδια για το γκάζι, το φρένο, το τιμόνι κλπ κλπ. Κατά μία έννοια λοιπόν, το πετάλι για το γκάζι/φρενο κλπ, στην ουσία "κρύβει" τον μηχανισμό της επιτάχυνσης του αυτοκινήτου. Ετσι, όλος ο κόσμος ξέρει να οδηγάει πολύ εύκολα, χωρίς να γνωρίζει πως λειτουργεί ένα αυτοκίνητο. Αυτός ο μηχανισμός γίνεται μέσω μιας function. Η function περιέχει όλα αυτά τα σχέδια της επιτάχυνσης ώστε oταν πατάμε το πετάλι του γκάζι, το αυτοκίνητο να επιταχύνει. Υπάρχει επίσης μια άλλη function, η οποία περιέχει τον μηχανισμό για να σταματάει το αμάξι όταν πετάμε φρένο. Ακόμα, υπάρχει κι άλλη function που περιέχει τον μηχανισμό για να στρίβουμε το αμάξι με το τιμόνι. Όλες αυτές οι functions περιέχουν του μηχανισμούς για λειτουργίες του αυτοκινήτου. Τον οδηγό όμως, δεν τον ενδιαφέρει ο μηχανισμός αλλά η λειτουργία της function (δηλαδή να γκαζώνει, να φρενάρει και στρίβει). Class, λοιπόν, είναι μια συλλογή - ένα σπίτι - που στεγάζει αυτές τις functions, σαν το σπίτι του μηχανικου που έχει στο γραφείο του τα σχέδια του νέου μοντέλου αμαξιού της εταιρίας. Σε μία class μπορείς να έχεις μέσα της, 1 ή και περισσότερες member function. Όλες τους όμως, όπως είπαμε, θα περιέχουν μηχανισμούς που αφορούν λειτουργίες της Class. Όπως τα σχέδια του μηχανικού (σχεδιο για το γκαζι, σχέδιο για το φρενο κλπ) λειτουργίες του αυτοκινήτου. Όμως δεν μπορούμε να οδηγήσουμε τα σχέδια. Δεν μπορούμε να πατήσουμε το γκάζι πάνω στην κόλα Α2 που σχεδιάσαμε το γκάζι και να πάρει κινηθεί το αυτοκίνητο στη ζωγραφιά-σχέδιο. Πρέπει λοιπόν, κάποιος, να κατασκευάσει ένα πραγματικό αυτοκίνητο. Να περάσει από το χαρτί στην πραγματικότητα της ζωής μας. Έτσι, λοιπόν, αφού έχουμε τα μηχανολογικά σχέδια (δηλαδή την Class), φτιάχνουμε ένα αυτοκίνητο (δηλαδή ένα Object της Class). Φυσικά μπορούμε να φτιάξουμε 1 και περισσότερα αυτοκίνητα από ένα σχέδιο - άρα μπορούμε να φτιάξουμε ένα ή και περισσότερα Objects από μια Class. Η Class - δηλαδή τα μηχανολογικά σχέδια του αυτοκινήτου - περιέχουν τους μηχανισμούς (member functions) οι οποίεις θα υλοποιηθούν με την σειρά τους στο αυτοκίνητο που θα κατασκευάσουμε (δηλαδή το Object).Όταν πατάτε το γκάζι, τότε στην ουσία καλείται μία λειτουργία του αυτοκινήτου : την επιτάχυνση. Ανάλογα, στην C++, στέλνετε μυνήματα στο Object (αυτοκίνητο)για να επιταχύνει. Αυτή η διαδικασία της "κλήσης" ενός μηχανισμού/ μιας λειτουργίας(member function) της Class(μηχανολογικα σχέδια αυτοκινήτου) για να υλοποιηθεί από το Object(αυτοκίνητο) λέται member-function call ή αλλιώς Object Service Request. Κάθε αυτοκίνητο όμως, έχει το δικό του χρώμα, το δικό το σαλόνι, το δικό του max ταχύτητας, ξεχωριστή δεξαμενή καυσιμων κλπ. Μπορεί να είναι αυτοκίνητο που προέρχεται από το ίδιο μηχανολογικό σχέδιο. Toyota Auris θα βρείς σε πολλά μοντέλα. Όλα όμως προέρχονται από το ίδιο σχέδιο. Είναι απλώς διαφορετικά μοντέλα. Σε τι διαφέρουν δηλαδή ; Στις ιδιότητες (attributes). Αρα κάθε object που μπορεί να προέρχεται από μια Class μπορεί να διαφοροποιείται από ένα Object που προέρχεται από την ίδια Class, γιατί θα έχει διαφορετικά attributes (πχ διαφορετικό χρωμα). Προσέξτε όμως, οι ιδιότητες (attributes) ΔΕΝ ΕΙΝΑΙ μέρος του μηχανολογικού σχεδιού (δηλαδή της Class) αλλά είναι μέρος που στεγάζεται στο Object καθ' αυτό. Εν ολίγης, function (member function) : Ο μηχανισμός που υλοποιεί τις διάφορες λειτουργίες ενός προγράμματος. Το "πώς" της υλοποιεί, παραμένει "κρυφό" μέσα στην function και δεν σας ενδιαφέρει. Class είναι μια συλλογή απο πληροφορίες - λειτουργίες - μηχανισμούς, που "κρύβονται" μέσα στις member functions (δηλαδή functions που είναι μέλη της Class). Object είναι αντίτυπο (instance) - υλοποίηση της Class. πχ ενα αυτοκίνητο που έχει μονο γκάζι. πχ ένα αλλό που έχει και γκαζι και φρένο. Attributes είναι οι ιδιότητες του κάθε αντικειμένου ξεχωριστά (πχ το χρώμα (βλεπε Admin του TheLab) του). ## 0x01 --Classes ## ############# Είναι μία συλλογή από ιδιότητες. Φανταστείτε κάτι σαν τα σχέδια ενός μηχανικού. Απλά να ξέρετε ότι είναι μία συλλογή από ιδιότητες, τίποτα παραπάνω για την ώρα. Εύκολος ορισμός ε ; Κάθε class έχει 2 πράγματα: Μεταβλητές και Συναρτήσεις (Variables & Functions) Οι variables(μεταβλητές) που βρίσκονται μέσα σε μία class έχουν ένα ειδικό όνομα. Λέγονται: Member Variables ή Data Members. Οι Συναρτήσεις( functions) που βρίσκονται μέσα σε μία class έχουν κι αυτές ένα ειδικό όνομα Λέγονται: Member Functions ή Methods. ############################################################ Παράδειγμα: πχ έστω ότι έχουμε μια class Gatoula() Αυτή πιθανόν να περιέχει τα εξής: Member Variables: int ilikia, char xroma, char ratsa, char xroma_mation, klp klp Member Functions: void Niaourisma(), void Upnos(), void Perpatima() , void Kunigi_Pontikion() klp klp Παράδειγμα μιας class ακολλουθεί ακριβως παρακάτω :::: class Gatoula { unsigned int IlikiaTis; unsigned int VarosTis; void Niaou(); }; ## ## 0x02 --Ονοματολογία και στιλ ## ###################### Κάθε προγραμματιστής έχει υιοθετήσει ένα δικό του coding style. Ετσι και εδώ θα πρέπει να κατασταλάξετε στην ονομασία των Data Members και των Functions. Διαλέξτε ένα στιλ: [1] . itsAge [2] . myAge [3] . m_Age (το m από την λέξη member) [4] . mAge ## ## 0x03 --Object ## ############# Εχοντας ήδη φτιάξει μια Class με το ονομα Gatoula, τώρα θα φτιάξουμε μία πραγματική γάτα. Παράδειγμα δήλωσης αντικειμένου Gatoula Lucifer //defines an object Lucifer ############################################################### O Lucifer είναι πλέον ένα αντικείμενο τύπου Gatoula, δηλαδή, περιέχει όλες τις ιδιότητες (μην ξεχνας ότι η class είναι μία συλλογή πληροφοριών) της Class που προέρχεται. μπορεί να νιαουρίσει, πχ Lucifer.Niaou(); δώστου μια ηλικία, πχ Lucifer.IlikiaTis = 5; ############################################################### Όταν φτιάχνεις μια class σκέψου ότι φτιάχνεις τον κόσμο. Για να φτιάξεις μία γάτα στο πρόγραμμα σου πρέπει να τις δώσεις τα χαρακτηριστικά που έχει στον πραγματικό κόσμο. Δηλαδή να της δώσεις όνομα, χρώμα, ράτσα, πόδια, ουρά, μάτια, μύτι, αυτιά κλπ . Μετά να τις δώσεις αυτά που μπορεί να κάνει δηλαδή να περπατάει, να κοιμαται, να τρώει, να νιαουρίζει, να κυνηγάει ποντίκια, να πηδάει ψηλά να εχει 7 ζωές (λεμε τώρα) κλπ. Η class είναι οι οδηγίες για να φτιάξεις πολλές γάτες στ ο προγραμμα σου. Οι οντότητες που θα πάρουν σάρκα και οστά μέσω των οδηγιών της class είναι το αντικείμενο. Το αντικείμενο χρησιμοποιεί τις ιδιότητες και τα χαρακτηριστικά μιας class. Οπότε μπορείς να φτιάξεις διάφορα αντικείμενα/διάφορες γάτες δίνοντας τιμές στις ιδιοτήτες μιας Class. Πχ ο ένας γάτος θα έχει ηλικία 3, ο άλλος 4, και ο άλλος 3. Αλλά ο τελευταίος θα έχει μαύρο χρώμα, ενώ ο πρώτος θα έχει καφέ. Επίσης ο μεσαίος γάτος θα μπορεί να κυνηγάει ποντίκια, αλλό οι άλλοι δύο όχι. Όλα αυτά έχουν να να κάνουν με μία class που έχεις ορίσει την γάτα στο πρόγραμμα σου. Στα αντικείμενα αυτής, δίνεις τις ιδιότητες της ## 0x04 --Public & Private members ######################## Εξορισμού, όλα τα members της class είναι private. Ας δούμε ένα παράδειγμα με public : /* Example#include <iostream>using namespace std;class Cat // Defining Class me to onoma Cat{ public: // public access int myAge; //member var int myWeight; //member var}; // <--------- min ksexaseis to erotimatiko sto telos tis Classint main(){ Cat Kastrato; define a Object of the Class Kastrato.myAge = 5; // O Kastrato einai pleon 5 xronon cout << "O Kastrato einai " << Kastrato.myAge << " xronon" << endl; return 0;} ##################################################################### ## Αν αφαιρέσω την λέξη public, τότε δεν μπορώ μες την main να γράψω Kastrato.myAge = 5; Ας δούμε ένα παράδειγμα με Private (πιο ασφαλές). Για να αλλάξω τιμή στα private members θα πρέπει να φτιάξω public methods που να έχουν πρόσβαση σε αυτά. Αυτές τις ονομάζουμε accessors (access = πρόσβαση) #include <iostream>using namespace std;class Cat{ public: int GetAge() const { return myAge; } // accessor function inline void SetAge( int age) { myAge = age; } // accessor function inline void Niaou(); // public method private: int myAge; // private data member;};int main(){ Cat Arpiah; Arpiah.SetAge(5); cout << "I Ilikia tis Arpiah einai : " << Arpiah.GetAge() << endl; return 0;} ## ## 0x05 --Constructor / Destructor ## ######################## Κάθε αντικείμενο έχει εναν Constructor και εναν Destructor (υπάρχει και ο Copy Constructor αλλα δεν θα σχολιασω τιποτα γι δ'αυτον). Αυτά είναι κάτι σαν την προεπιλεγμένη τιμή που παίρνει το αντικείμενο με το που δημιουργείται. Ειναι τι θελουμε να γινετε οταν δημιουργειται και οταν καταστρέφεται. πχ Εστω οτι θέλουμε εξορισμού, ο γάτος μας να είναι 9 χρονών με το που τον αρχικοποιουμε: #include <iostream>using namespace std;class Gatos{ public: Gatos ( int arxiki_ilikia) { myAge = arxiki_ilikia; } // Constructor; i allios Gatos() { myAge = 9 ; } i allios Gato(): myAge(9) {} ~Gatos() {} // Destructor - den kanei tipota int getAge() const { return myAge; } // accessor function int setAge( int age ) { myAge = age ; } // accessor function private: int myAge;};int main(){ Gatos Garfield(9); cout << "O Garfield einai " << Garfield.getAge() << " xronon " << endl; Garfield.setAge(5); cout << "Tora omos einai : " << Garfield.getAge() << " xronon " << endl; return 0;} ## ## 0x06 --Inheritance ## ############### Εχουμε τα θηλαστικα. Αυτα κοιμουνται και τρωνε. Ο Σκυλος ειναι θηλαστικο. Ο Σκυλος γαβγιζει. [Ο Σκυλος ειναι θηλαστικο. Ο Σκυλος τρωει, κοιμαται και γαβιζει. Το πιασες ; Ορισμός ---> Class DerivedClass : accessType BasedClass Παράδειγμα: #include <iostream>using namespace std;class Thilastiko{ public: Thilastiko(): itsAge(0) {} ~Thilastiko() {} int GetAge() const { return itsAge; } void SetAge(int age) { itsAge = age; } void Sleep() const { cout << "Shhhh. I am sleeping\n"; } protected: int itsAge;};class Skylos : public Thilastiko{ public: void Bark() const { cout << "Gav Gav Gav!!!\n"; }};int main(){ Skylos Rudolf; cout << "O Rudolf einai : " << Rudolf.GetAge() << " xronon " << endl; Rudolf.SetAge(3); cout << "O Rudolf einai : " << Rudolf.GetAge() << " xronon " << endl; Rudolf.Bark(); return 0;} Overloading Constructors Παράδειγμα #include <iostream>using namespace std;class HeavyMetalBand{ public: HeavyMetalBand(){} // Constructor HeavyMetalBand( int members ):itsMembers(members) {} // i allios meta to (int members) { itsMembers = members; } ~HeavyMetalBand() {} // Destructor // Accessors int GetMembers() const { return itsMembers; } void SetMembers( int mMembers ) { itsMembers = mMembers; } // Other Methods void PlayMusic() const { cout << "...Keep on rocking in the free world...\n"; } protected: int itsMembers;};class LeadGuitar : public HeavyMetalBand{ public: // Constructors & Overloaded Constructors LeadGuitar() {} LeadGuitar(int yearsOfExperience ) { itsYearsExp = yearsOfExperience;} // A Tropos me = LeadGuitar(int yearsOfExperience, int Age):itsYearsExp(yearsOfExperience), itsAge(Age) {} // B Tropos me : LeadGuitar(int Age, int Guitars, int SongsComposed):itsAge(Age), itsGuitars(Guitars), itsSongs(SongsComposed) {} ~LeadGuitar() {} //Accessors int GetExp() const { return itsYearsExp; } int GetAge() const { return itsAge; } int GetNumOfGuitars() const { return itsGuitars; } int HowManySongs() const { return itsSongs; } void SetExp( int mYears ) {itsYearsExp = mYears;} // Den yparxei o B Tropos void SetAge( int mAge ) { itsAge = mAge; } // kathos edo exoume Methods (Member Function) void SetGuitars ( int mGuitars ) { itsGuitars = mGuitars; } // kai oxi constructors kai destructors void SetSongs( int mSongs) { itsSongs = mSongs; } // o A Tropos paizei mono me Constructors kai Destructors // Other Methods void RiffEmAll() const { cout << "Metal up your ass! Treat me, baby ... \n"; } private: int itsYearsExp, itsAge, itsGuitars, itsSongs;};int main(){ HeavyMetalBand Metallica(4); cout << "Metallica is consisted of " << Metallica.GetMembers() << " members " << endl; LeadGuitar KirkHammet; LeadGuitar DaveMustaine(17,32); LeadGuitar JamesHetfield(17,11,52); //He is Rythm Guitar anyway... // cout whatever ... I am boring 4 typing so long... You got the point, don't ya ? return 0;} ##################################################################### Overriding & Hiding & Calling Base Methods Overriding: Εχω μια method στην base class Thilastiki με το ονομα Speak() { cout << "To thilastiko milaei" } και μετα φτιαχνω μια derived class Skylos και θελω να βαλω κι εδω μια method Speak() υπάρχει όμως ήδη απο πριν αλλα εγω θελω ο σκυλος να κανει Γαβ Γαβ και οχι να λεει: "Το θηλαστικο μιλάει" ετσι φτιαχνω μια ίδια method, με [1] --> Ιδιο ή διαφορετικο return type [2] --> Ιδιο όνομα [3] --> Ιδια λίστα παραμέτρων [4] --> Με την λέξη const (αν έγινε η χρηση της στην base class method) [5] --> Με διαφορετικό implementation ( πχ cout << cout << "Gav Gav Gav" ) ##################################################################### Παράδειγμα: Overriding class Mammal{ public: // constructors Mammal() {} ~Mammal() {} //Other methods void Speak()const { cout << ?Mammal sound!\n?; } void Sleep()const { cout << ?shhh. I’m sleeping.\n?; }};class Dog : public Mammal{ public: // Constructors Dog(){} ~Dog(){} // Other methods void WagTail() const { cout << ?Tail wagging...\n?; } void BegForFood() const { cout << ?Begging for food...\n?; } void Speak() const { cout << ?Woof!\n?; } // Overriding Base Method Speak}; Hiding: Αν η base class method Speak έχει διάφορες παραλαγες τις (δηλαδη έχει γίνει overload) πχ Speak(Greek), ή Speak(int lekseis, int protaseis) και φυσικά υπάρχει και η απλή η Speak(), τότε: αν κάνω overriding την Speak() , τότε αυτόματα, κρύβω τις παραλαγές τις δεν μπορώ να τις καλέσω από την derived class. Παράδειγμα: Hiding ## class Mammal{ public: Mammal(){} ~Mammal(){} void Speak() const { cout << "To thilastiko milise kai eipe tin teleutaia tou leksi" ; } void Speak(int words) { cout << "To thilastiko milise kai eipe tis " << words << " teleutaies tou lekseis"; }};class Dog: public Mammal{ public: Dog() {} ~Dog() {} void Speak() const { cout << "O skylos milise kai eipe tin teleutaia tou leksi"; }};int main{ Mammal Platupodaros; Dog Skylaki; Platupodaros.Speak(); Sylaki.Speak() Platupodaros.Speak(5); // Skylaki.Speak(5) ----> Θα ειναι ερρορ γιατι έχει γινει hidden To πιάσατε ; Οποτε υπάρχει ο κανόνας: Αν κανεις override μια method που εχει γινει overload , τοτε εχει 2 επιλογες: [1] --> Θα κανει override αυτη που θες και αυτοματα η αλλες θα γινουν hidden [2] --> Θα τις κανεις ολες αναγκαστικα overriding χωρις να μεινει καμια hidden #include <iostream>using namespace std;class Mammal{ public: Mammal(){} ~Mammal(){} void Speak() const { cout << "To thilastiko milise kai eipe tin teleutaia tou leksi\n" ; } void Speak(int words) { cout << "To thilastiko milise kai eipe tis " << words << " teleutaies tou lekseis\n"; }};class Dog: public Mammal{ public: Dog() {} ~Dog() {} void Speak() const { cout << "O skylos milise kai eipe tin teleutaia tou leksi\n"; }int main(){ Mammal Platupodaros; Dog Skylaki; Platupodaros.Speak(); Skylaki.Speak(); Platupodaros.Speak(5); Skylaki.Mammal::Speak(5); // Ayto paizei mia xara omos! Xexexexe! // Skylaki.Speak(5) ----> Θα ειναι ερρορ γιατι έχει γινει hidden return 0;} #include <iostream>using namespace std;class Mammal{ public: Mammal(){} ~Mammal(){} void Speak() const { cout << "To thilastiko milise kai eipe tin teleutaia tou leksi\n" ; } void Speak(int words) { cout << "To thilastiko milise kai eipe tis " << words << " teleutaies tou lekseis\n"; }};class Dog: public Mammal{ public: Dog() {} ~Dog() {} void Speak() const { cout << "O skylos milise kai eipe tin teleutaia tou leksi\n"; }int main(){ Mammal Platupodaros; Dog Skylaki; Platupodaros.Speak(); Skylaki.Speak(); Platupodaros.Speak(5); Skylaki.Mammal::Speak(5); // Ayto paizei mia xara omos! Xexexexe! // Skylaki.Speak(5) ----> Θα ειναι ερρορ γιατι έχει γινει hidden return 0;} Bypass the Hiding Τι γίνετε όμως βαριέστε να κάνετε overriding όλες τις methods (καλό είναι να μην βαριέστε όμως) ; Πώς θα χρησιμοποιήσετε την method Speak(int words) στο Skylaki ; ; ; ####################################################################### ## Παράδειγμα: bypass the hiding #include <iostream>using namespace std;class Mammal{ public: Mammal(){} ~Mammal(){} void Speak() const { cout << "To thilastiko milise kai eipe tin teleutaia tou leksi\n" ; } void Speak(int words) { cout << "To thilastiko milise kai eipe tis " << words << " teleutaies tou lekseis\n"; }};class Dog: public Mammal{ public: Dog() {} ~Dog() {} void Speak() const { cout << "O skylos milise kai eipe tin teleutaia tou leksi\n"; }int main(){ Mammal Platupodaros; Dog Skylaki; Platupodaros.Speak(); Skylaki.Speak(); Platupodaros.Speak(5); Skylaki.Mammal::Speak(5); // Ayto paizei mia xara omos! Xexexexe! // Skylaki.Speak(5) ----> Θα ειναι ερρορ γιατι έχει γινει hidden return 0;} ######################################################################## ## ## Το παραπάνω κόλπο είναι : derived_class.base_class::base_method(parameter) ## Προσοχή όταν κάνετε overriding να μην ξεχνάτε την λέξη const αν υπάρχει [\b] ########################################################################## Virtual Methods (How deep is the rabbit's hole) Ξέρουμε ότι ο Σκύλος κληρονομεί τις ιδιότητες (data & methods) των θηλαστικών. Λογικό και κατανοητό. Η C++ όμως παρέχει και το εξής: pointer τύπου δεδομένων base class που κρατάει ως τιμή την διεύθυνση μνήμης της derived class. Δηλαδή pointer τύπου Θηλαστικού, που κρατάει την διεύθυνση του Σκύλου. Τωρα θα πείτε: Ε (;;; Δηλαδή: Thilastiko *ptr = new Skylos // ενώ μέχρι τώρα γράφαμε : Skylos ptr; Τι κάνει ^^^ : Φτιάχνει ένα αντικείμενο Skylos, και έναν pointer που δέχεται τιμές τύπου Thilastiko. Ο Σκύλος όμως είναι θηλαστικό, άρα είμαστε μια χαρά! (αφού class Skylos : public Thilastiko ) Αλλάζει επίσης και ο τρόπος που καλούμε methods. Παλιά (που λεει ο λογος) : ptr.method(); Τώρα : ptr -> method(); Πιο είναι το πρόβλημα όμως ; Αν κάνεις overriding μια method, τότε αντί να καλέσει την method απο την derived class θα καλέσει την hidden method της base class. Δες το ακόλουθω παράδειγμα: ##Example - Παλιος Τρόπος############################################################ #include <iostream>using namespace std;class Thilastiko{ public: void Speak() { cout << "Thilastiko milaei" << endl; }};class Skylos : public Thilastiko{ public: void Speak() { cout << "Woof Woof Woof" << endl;}};int main(){ Skylos Beetoven; Beetoven.Speak(); return 0;} ########################################################################### ## Output: Woof Woof Woof ########################################################################### Example - Καινούριος Τρόπος (δες την διαφορα στο output) #include <iostream>using namespace std;class Thilastiko{ public: void Speak() const { cout << "Thilastiko milaei" << endl; }};class Skylos : public Thilastiko{ public: void Speak() const { cout << "Woof Woof Woof" << endl;}};int main(){ Thilastiko *Beetoven = new Skylos; Beetoven -> Speak(); // palia grafame Beetoven.Speak(); return 0;} ########################################################################## ## Output: Thilastiko milaei ########################################################################### Όπως βλέπεις δεν πιάνει το overriding. Αυτό συμβαίνει γιατί ο pointer επειδή είναι τύπου Thilastiko βλέπει όλες τις function του thilastiko (aka της base class) . Ο τρόπος για να χρησιμοποιήσεις το overriding όπως και πριν είναι ένας: Να κάνεις την method speak, τύπου virtual. Αυτό είναι, virtual. Virtual κάνεις όσες methods θέλεις/ξέρεις από πριν να κάνεις overriding Σωστο παράδειγμα με Virtual Functions #include <iostream>using namespace std;class Thilastiko{ public: Thilastiko() {} // constructor virtual ~Thilastiko() {} // Destructor PANTA VIRTUAL virtual void Speak() const { cout << "Thilastiko milaei" << endl; }};class Skylos : public Thilastiko{ public: Skylos() {} // constuctor virtual ~Skylos() {} //destructor PANTA VIRTUAL virtual void Speak() const { cout << "Woof Woof Woof" << endl;}};int main(){ Thilastiko *Beetoven = new Skylos; Beetoven -> Speak(); // palia grafame Beetoven.Speak(); return 0;} ########################################################################## ## Output: Woof Woof Woof ########################################################################## Προσβαση στη hidden virtual base method Example #include <iostream>using namespace std;class Thilastiko{ public: Thilastiko() {} // constructor virtual ~Thilastiko() {} // Destructor PANTA VIRTUAL virtual void Speak() const { cout << "Thilastiko milaei" << endl; }};class Skylos : public Thilastiko{ public: Skylos() {} // constuctor virtual ~Skylos() {} //destructor PANTA VIRTUAL virtual void Speak() const { cout << "Woof Woof Woof" << endl;}};int main(){ Thilastiko *Beetoven = new Skylos; Beetoven -> Speak(); // palia grafame Beetoven.Speak(); Beetoven->Thilastiko::Speak(); // palia grafame Beetoven.Thilastiko::Speak(); return 0;} ######################################################################### ## Output: Woof Woof Woof ## Thilastiko milaei ######################################################################### Example Dynamic Binding #include <iostream>using namespace std;class Thilastiko{ public: Thilastiko() {} // constructor virtual ~Thilastiko() {} // Destructor PANTA VIRTUAL virtual void Speak() const { cout << "Thilastiko milaei" << endl; }};class Skylos : public Thilastiko{ public: Skylos() {} // constuctor virtual ~Skylos() {} //destructor PANTA VIRTUAL void Speak() const { cout << "Woof Woof Woof" << endl;}};class Cat : public Thilastiko{ public: Cat() {} // constructor virtual ~Cat() {} //Destructor PANTA VIRTUAL virtual void Speak() const { cout << "Niaou Niaou Niaou" << endl; }};class Horse : public Thilastiko{ public: Horse() {} // constructor virtual ~Horse() {} //Destructor PANTA VIRTUAL virtual void Speak() const { cout << "Xlimintrisma" << endl; }};class Cow : public Thilastiko{ public: Cow() {} // constructor virtual ~Cow() {} // Destructor PANTA Virtual virtual void Speak() const { cout << "Mooooo Mooooo Mooooo" << endl; }};int main(){ // Static // Thilastiko *ptr = new Skylos; // Dynamic Binding Runtime //The Reason you learn subclass pointer definition // Thilastiko *pinakas[5]; // Pinakas (se periptosi pou den to ksereis, ola ta arrays einai pointers) // pou krataei dedomena typoy Thilastiko Thilastiko *ptr; // pointer tupou Thilastiko , edo 8a grafame Thilastiko *ptr = new Skylos int choice, i; cout << "Please choose: " << endl; for(i=0; i<5; i++) { cout << " Skylos(1) Gata(2) Alogo(3) Agelada(4) :"; cin >> choice; switch (choice) { case 1: ptr = new Skylos; // to ptr einai idi Thilastiko *ptr break; case 2: ptr = new Cat; break; case 3: ptr = new Horse; break; case 4: ptr = new Cow; break; default: ptr = new Thilastiko; break; } pinakas[i] = ptr; } for(i=0; i<5; i++) { pinakas[i] -> Speak(); } return 0;} Output: Please choose: Skylos(1) Gata(2) Alogo(3) Agelada(4) :1 Skylos(1) Gata(2) Alogo(3) Agelada(4) :2 Skylos(1) Gata(2) Alogo(3) Agelada(4) :3 Skylos(1) Gata(2) Alogo(3) Agelada(4) :4 Skylos(1) Gata(2) Alogo(3) Agelada(4) :5 Woof Woof Woof Niaou Niaou Niaou Xlimintrisma Mooooo Mooooo Mooooo Thilastiko milaei Σημαντικά πράγματα που πρέπει να ξέρεις για τις virtual functions Οταν γράφεις: Thilastika *ptr = new Dog; Tότε φτιάχνετε ένα κουτί μέσα στην μνήμη του υπολογιστή (heap) το οποίο είναι χωρισμένο σε δύο μέρη το ένα μέρος είναι το 1)Thilastiko Part και το δευτερο μέρος είναι 2)Dog Part. Αυτά τα δύο βρίσκονται μέσα στο κουτί το οποίο κουτί είναι το αντικείμενο Dog Όταν κανω virtual μια method της base class Thilastiko πχ την void Speak(), παω την αλλαζω σε virtual void Speak() τότε το αντικείμενο πρέπει να είναι σε θέση να ακουλοθεί τα ίχνη αυτής της virtual void Speak(). Πώς το πετυχaiνει ; Αρχικά όπως μάθαμε πιο πανω, καλείται ο Base Constructor (aka Thilastika Constructor) καλείται αυτός, τότε ο compiler φτιάχνει έναν πίνακα που των χωρίζει σε 2 μέρη (οπως πριν) αυτός ο πίνακας λέγεται v-table του Thilastika. Τι περιέχει αυτός ο πίνακας όμως ; Είναι χωρισμένος σε 2 μέρη. Το ένα μέρος του λέγεται 1)Thilastiko και το άλλο λέγεται 2)v-ptr Το v-ptr είναι ο pointer που δείχνει την virtual method. Δηλαδη κρατάει την διεύθυνση της virtual method (& Speak) Μετά καλείται ο Derived Constructor (aka Dog Constructor) και ο compiler κάνει παλι το ίδιο. Φτιάχνει έναν πίνακα που λέγεται v-table του αντικεμενου Dog Αυτός είναι χωρισμένος σε δυο μέρη. Το 1)Dog και το αλλο 2)v-ptr To v-ptr δείχνει στην virtual method Speak της Base Class , δηλαδή δείχνει στο &Mammal:Speak ενώ οι άλλες derived έχουν διεύθυνση &Dog:Move. Προσεχε, το v-ptr δείχνει μόνο στην virtual method void Speak. ############################################################################# ## Κατι ακομα: Αν εχεις μια derived method Move που δεν υπάρχει στην base class τότε δεν μπορεις να την καλεσεις ## με κανεναν τροπο. Τι θα κανεις ; Θα το μαθεις μετα, λεγεται typeCasting. ############################################################################# ## 0x08 --Multiple Inheritance ## #################### Εχεις δυο classes: Αλογο και Πτηνό. Θες να φτιαξεις μια τριτη class Πήγασσος. Τι ειναι ομως αυτο ; Πουλί ή Αλογο ; είναι και απο τα δύο. Γράφεται: class Pigasos : public Alogo, public Pouli Παράδειγμα #include <iostream>using namespace std;class Alogo{ public: Alogo() {} // constructor virtual ~Alogo() {} // destructor // Accessors Methods // variemai na grafo // Other Methods virtual void xlimintrisma() const { cout << "Xlimintrizo\n"; }};class Pouli{ public: Pouli() {} // Constructor virtual ~Pouli() {} // Destructor // Accessors // variemai na grafo // Other Methods virtual void petao() const { cout << "Petao\n"; }};class Pigasos : public Alogo, public Pouli{ public: Pigasos() {} // constructor virtual ~Pigasos() {} // destructor};int main(){ Alogo * ptrAlogo; Pouli * ptrPouli; Pigasos * ptrPigasos; int choice, i; cout << "Please choose:\n"; cout << "Alogo(1), Pouli(2), Pigasos(3) : "; cin >> choice; switch (choice) { case 1: ptrAlogo = new Alogo; break; case 2: ptrPouli = new Pouli; break; case 3: ptrPigasos = new Pigasos; break; default: cout << "Error input. Program terminated. . . ."; return -1; break; } if (choice == 1) { ptrAlogo -> xlimintrisma(); } if (choice == 2) { ptrPouli -> petao(); } if (choice == 3) { ptrPigasos -> xlimintrisma(); ptrPigasos -> petao(); } return 0;} Virtual Classes Animal Horse Bird <--- Horse and Bird derived απο την Animal, και μεταξυ τους εχουν ως μωρό τους την Pegasus Pegasus Για να εξασφαλίσεις οτι classes προέρχονται από την ίδια κοινή base class, τις κάνω virtual Αφού ξέρεις από πριν ότι από μια class θα ξεπεταχτούν αρκετές subclasses και όχι απλά μόνο μία, τότε είναι καλή τακτική να τις κάνεις virtual. ## Example #include <iostream>using namespace std;enum XROMA { ASPRO, MAVRO, KAFE };class Zoa // common base class for both Alogo & Pouli{ public: Zoa() {} Zoa( int age ):itsAge(age) {} virtual ~Zoa(){} virtual int GetAge() const { return itsAge; } virtual void SetAge( int mAge) { itsAge = mAge; } protected: int itsAge;};class Alogo : virtual public Zoa{ public: Alogo() {} Alogo(int age, XROMA color):itsAge(age), itsColor(color) {} virtual ~Alogo() {} virtual int GetAge() const { return itsAge; } virtual void SetAge( int mAge ) { itsAge = mAge; } virtual XROMA GetColor() const { return itsColor; } virtual void SetColor( XROMA mColor) { itsColor = mColor; } protected: XROMA itsColor; int itsAge;};class Pouli : virtual public Zoa{ public: Pouli() {} Pouli(int age, XROMA color): itsColor(color), itsAge(age) {} virtual ~Pouli() {} virtual int GetAge() const { return itsAge; } virtual void SetAge( int mAge) { itsAge = mAge; } virtual XROMA GetColor() const { return itsColor; } virtual void SetColor( XROMA mColor ) { itsColor = mColor; } protected: XROMA itsColor; int itsAge;};class Pigassos : public Alogo, public Pouli{ public: Pigassos() {} Pigassos(int age, XROMA body, XROMA ftera) { body = Alogo::itsColor; ftera = Pouli::itsColor; itsAge = age; } virtual ~Pigassos() {} virtual int GetAge() const { return itsAge; } virtual void SetAge( int mAge) { itsAge = mAge; } virtual XROMA GetColorBody() const { return Alogo::itsColor; } virtual void SetColorBody(XROMA mbody) { Alogo::itsColor = mbody; } virtual XROMA GetColorFtera() const { return Pouli::itsColor; } virtual void SetColorFtera(XROMA mftera) { Pouli::itsColor = mftera; } protected: int itsAge;};int main(){ Alogo *pAlogo = new Alogo(2,KAFE); Pigassos *pPigas = new Pigassos(12, ASPRO, ASPRO); cout << "O Pigassos exei ilikia " << pPigas -> GetAge() << " xroma somatos " << pPigas -> GetColorBody() << " xroma fteron " << pPigas -> GetColorFtera() << endl; cout << "\nTo alogo exei xroma: " << pAlogo -> GetColor () ; pAlogo -> SetColor( MAVRO ); cout << "\nTo alogo exei xroma: " << pAlogo -> GetColor () ; cout << "\nO Pigassos exei xroma somatos: " << pPigas -> GetColorBody() << endl; return 0;} ####################################################################### Abstract Class Συχνα χρειαζεται να φτιαξετε μια class σαν οδηγο. Δεν προκειται ποτε να φτιαξετε αντικειμενο για αυτη την αλλά θέλετε να την χρησιμοποιήσετε για να κρατάτε έναν οδηγό/πατούρα των methods πχ εχω μια class abstract που λεγεται Shape. Απο κει βγαζω 2 derived class Κυκλος και Ορθογωνιο για τον σχεδιασμό τους και τα γεωμετρικα χαρακτηριστικά χρησιμοποιηώ τις methods του Shape Πες τώρα ότι θελεις να φτιαξεις μια class Τετραγωνο. Αυτη όμως είναι μια ειδικη περίπτωση του ορθογωνίου οποότε γιατι να την ξαναγράφεις ; Αρκεί να την περάσεις (μεσω constructor overloading) στο ορθογώνιο Pure Function: Βάζουμε τις methods της Abstract Class. Μετα απο αυτο ειναι αδυνατο να δημιουργηθεί αντικείμενο της abstract class. Παράδειγμα // Method to be Override virtual void Draw() = 0; // pure function private:};// / / / / / / / / / / /// Derived Classes// /////////////////////////1st Derived Cycle //// //////////////////////class Circle : public Shape // publicaly access type of Shape{ public: Circle(int radius):itsRadius(radius) {} //call -> Circle(5) , 5=radius virtual ~Circle(){} // Override Shape Accessors virtual long GetArea() { return 3 * itsRadius * itsRadius; } // 3*5*5 = 75 virtual long GetPerim() { return 6 * itsRadius; } // 6*5 = 30 // Override Shape Method Draw virtual void Draw(); // 8a grapso meta ti 8a kanei, edo den exo xoro private: int itsRadius; // to 5 8a isxuei mono gia autin tin class. int itsCircumference;};// Implementation of Method Draw of the Circle classvoid Circle::Draw(){ cout << "Circle drawing routine here!\n";}/////////////////////////////////// 2i Derived Class Rectangle //// ///////////////////////////////class Rectangle : public Shape{ public: Rectangle(int len, int width):itsLength(len), itsWidth(width) {} //Rectangle(5,10) 5=length, 10 = width virtual ~Rectangle() {} // Overriding accessors virtual long GetArea() { return itsLength * itsWidth; } // 5*10 = 50 virtual long Getperim() { return 2*itsLength + 2*itsWidth; } // 5*2 + 10 *2 = 30 // New Methods !!!!! virtual int GetLength() { return itsLength; } // GetLength = 5, epeidi to 5 den pernaei (private) stin derived square 8a perasei i method virtual int GetWidth() { return itsWidth; } // GetWidth = 10. to 10 den pernaei (private) stin derived square 8a perasei i method // Overriding DrawMethod virtual void Draw(); // 8a tin anaptikso meta private: int itsLength; // epeidi den pernane auta, ( logos einai epeidi einai private) int itsWidth; // 8a peraso tis methods pou kratane autous tous arithmous (giati na min kano protected omos o anomalos ? )};// Implementation of Draw Method (Rectangle Class)void Rectangle::Draw() // zografizei ena tetragono me 5 kai 10{ for(int i =0; i<itsLength; i++) { for ( int j =0; j<itsWidth; j++) { cout << "x"; } cout << endl; }}// Special Class Square// Derived from Rectangle which derived from Shapeclass Square : public Rectangle{ public: Square(int len); // square(5) 5 = lenth Square(int len, int width); // square(5,5) 5=length=width ~Square() {} // Override accessors long GetPerim() { return 4 * GetLength(); } // 4*5 = 20};// paratiro oti edo den exo tis class:// GetArea// void Draw --> simantiko! Giati arage den tin exo ?// Implementation of Square Constructor// Overloading 1Square::Square(int len):Rectangle(len,len) {} //trofodoti to len tou square os Length=Width gia tin Rectangle//Overloading 2Square::Square(int len, int width):Rectangle(len,width) //trofodototei ta len kai width tou Square sta antistoixa tou Rectangle{ if( GetLength() != GetWidth()) // An to Rectangle exei width = length { cout << "This is not a rectangle\n"; }}int main(){ int choice; bool fQuit = false; Shape *pointer; cout << "(1)Circle (2)Rectangle (3)Square (0)Quit: "; cin >> choice; while(!fQuit) { switch(choice){ case 0: fQuit = true; break; case 1: pointer = new Circle(5); break; case 2: pointer = new Rectangle(4,6); break; case 3: pointer= new Square(5); break; default: cout << "Please enter a number [0-3]" << endl; break; } if( !fQuit){ pointer ->Draw(); fQuit = true; } delete pointer; pointer = 0; cout << endl; } return 0;} ## 0x9 --Static Function / Members ## ####################### Μερικές φορές θέλεις να έχεις μια μεταβλητή ή μια συνάρτηση (κάτι σαν globe) που να μπορεί να ελέγχει ανα πάσα στιγμή κάποια δεδομένα που σχετίζονται με αντικείμενα ίδιου τύπου (πχ εχεις 5 γάτες) Αυτό γίνεται με static functions καθώς μεσω αυτών μπορείς να τις καλέσεις είτε μεσω object είτε μεσω class Ακολουθεί ένα παράδειγμα όπου οι static functions, χρησιμοποιούν τις static variables. Αν τις τελευταιες τις βαλω public τότε θα είναι διαθεσιμες σε όλους. Συνηθίζεται να είναι είτε private είτε protected. Example #include <iostream>using namespace std;class Gata{ public: Gata() { PosesGates++; } // constructor ~Gata() { PosesGates--; } // destructor // Static Function // Accessed through Object or through Class itself static int GetPosesGates() { return PosesGates; } // ^^^ DEN EINAI POTE const private: static int PosesGates;};// Initialize staticint Gata::PosesGates = 0;int main(){ //Ftiaxno mia Gata Object Gata Lougritia; //Kalesma static meso Class cout << Gata ::GetPosesGates() << endl; //kalesma static meso Object cout << Lougritia.GetPosesGates() << endl; return 0;} ## 0x10 --Pointer to Function ## #################### ## Ειναι πολυ χρησιμο για δυναμική επιλογή του χρήστη ποια function θελει να καλέσει ## οποτε φτιαχνεις εναν pointer ο οποιος δειχνει σε μια function με ίδιο return type και παραμέτρους #################################################################### πχ int pros8esi (int x, int y) { return x + y; } είναι μια function φτιαχνω εναν pointer στην function : int (* pointer2Function) (int, int) μετα βαζω τον pointer να δειχνει στην συγκεκριμενη function: pointer2Function = pros8esi και για να καλεσω την συναρτηση, καλω τον pointer που δειχνει στην συναρτηση: pointer2Function(2,5) ########################################################################## Παράδειγμα: #include <iostream>using namespace std;void Tetragono (int &x, int &y){ x = x * x; y = y * y;}void Kubos (int &x, int &y){ int temp; temp = x; x = x * x; x = x * temp; temp = y; y = y * y; y = y * temp;}void Swap(int &x, int &y){ int temp; temp = x; x = y; y = temp;}void Read(int &TimiEna, int &TimiDuo){ cout << "Dose nea timi Ena: "; cin >> TimiEna; cout << "Dose nea timi Duo: "; cin >> TimiDuo;}void Print(int x, int y){ cout << "x: " << x << " y: " << y << endl;}int main(){ // pointer to function // which returns void // parameters : int , int void (* pFunc) (int &, int &); bool fQuit = false; int TimiEna = 1, TimiDuo = 2; int choice; while (!fQuit) { cout <<" (0)Quit (1)Allagi Timon (2)Tetragono (3) Kubos (4)Swap: "; cin >> choice; switch(choice) { case 1: pFunc = Read; break; case 2: pFunc = Tetragono; break; case 3: pFunc = Kubos; break; case 4: pFunc = Swap; break; default: fQuit = true; } if(!fQuit) { Print(TimiEna, TimiDuo); // kalo tin function Print me klisi Timis pFunc(TimiEna, TimiDuo); // kalei tin function pou deixnei apo prin meso tou pointer Print(TimiEna, TimiDuo); } } return 0;} Να σημειωσω οτι ειναι αρκετα επικυνδινο αυτο και πλεον (τουλαχιστον στο Qt) γινεται μεσω SLOTS - METHODS . Αν θέλετε κι αλλα πείτε μου. Αν έχετε διορθώσεις πείτε μου. Αν διαφωνείται με κάτι πείτε μου. Αν αν αν αν ... στείλτε ενα PM. Link to comment Share on other sites More sharing options...
eleven Ιούλιος 31, 2008 #2 Κοινοποίηση Ιούλιος 31, 2008 Ωραιος Πανέα ,χαράς το κουράγιο σου Ξεκίνησα να το διαβάσω με ενδιαφέρον.Οσο έγραφες για το μηχανολογικό σχέδιο σε καταλαβαινα ,μολις έπιασες την γάτα και το σκύλο ........:006: και το παράτησα (μαλον η Δήμητρα θα τα καταλάβει).Ελπίζω μόνο να μην μαλώσουν μεταξύ τους μεσα στο class και γίνει χαμός......... Y.g This is Sparta Link to comment Share on other sites More sharing options...
GeorgeVasil Ιούλιος 31, 2008 #3 Κοινοποίηση Ιούλιος 31, 2008 Ωραιος ο Πανεας... Nice Work! Οταν εχεις χρoνo Bαλτα με Size 2 για να διαβαζετε ποιο ευκολα.. Link to comment Share on other sites More sharing options...
Dr.Paneas Ιούλιος 31, 2008 Author #4 Κοινοποίηση Ιούλιος 31, 2008 Ωραιος Πανέα ,χαράς το κουράγιο σουΞεκίνησα να το διαβάσω με ενδιαφέρον.Οσο έγραφες για το μηχανολογικό σχέδιο σε καταλαβαινα ,μολις έπιασες την γάτα και το σκύλο ........:006: και το παράτησα (μαλον η Δήμητρα θα τα καταλάβει).Ελπίζω μόνο να μην μαλώσουν μεταξύ τους μεσα στο class και γίνει χαμός......... Y.g This is Sparta Χαχα που να δεις μετα που παντευεται η γατα με τον σκυλο και βγαζουν σκυλογατο (Polymorhism ) Να στε καλα παιδια. Link to comment Share on other sites More sharing options...
Recommended Posts
Archived
This topic is now archived and is closed to further replies.