Jump to content



C++ Object Oriented (Τα Βασικα)


Dr.Paneas

Recommended Posts

Λοιπον τελικα στις διακοπες δεν ειχα ιντερνετ οπως συμφωνησαμε. Αλλα ... Αλλα ...

Γραμμένο απο εμένα. Οι κώδικες εχουν γινει 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 Class

int 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 class
void 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 Shape
class 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 1
Square::Square(int len):Rectangle(len,len) {} //trofodoti to len tou square os Length=Width gia tin Rectangle

//Overloading 2
Square::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 static
int 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

Ωραιος Πανέα ,χαράς το κουράγιο σου

Ξεκίνησα να το διαβάσω με ενδιαφέρον.Οσο έγραφες για το μηχανολογικό σχέδιο σε καταλαβαινα ,μολις έπιασες την γάτα και το σκύλο ........:006: και το παράτησα (μαλον η Δήμητρα θα τα καταλάβει).Ελπίζω μόνο να μην μαλώσουν μεταξύ τους μεσα στο class και γίνει χαμός.........:D

Y.g This is Sparta

Link to comment
Share on other sites

Ωραιος Πανέα ,χαράς το κουράγιο σου

Ξεκίνησα να το διαβάσω με ενδιαφέρον.Οσο έγραφες για το μηχανολογικό σχέδιο σε καταλαβαινα ,μολις έπιασες την γάτα και το σκύλο ........:006: και το παράτησα (μαλον η Δήμητρα θα τα καταλάβει).Ελπίζω μόνο να μην μαλώσουν μεταξύ τους μεσα στο class και γίνει χαμός.........:D

Y.g This is Sparta

Χαχα που να δεις μετα που παντευεται η γατα με τον σκυλο και βγαζουν σκυλογατο (Polymorhism ) :p

Να στε καλα παιδια.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Δημιουργία...

Important Information

Ο ιστότοπος theLab.gr χρησιμοποιεί cookies για να διασφαλίσει την καλύτερη εμπειρία σας κατά την περιήγηση. Μπορείτε να προσαρμόσετε τις ρυθμίσεις των cookies σας , διαφορετικά θα υποθέσουμε ότι είστε εντάξει για να συνεχίσετε.