HelloAge Class

Υποκλάσεις (subclasses)

Υποκλάσεις στηρίζονται σε κάποια άλλη κλάση Α της οποίας αποτελούν ένα είδος συμπληρώματος ή εξειδίκευσης. Επεκτείνουν τις ιδιότητες της Α και κάνουν κάτι επιπλέον. Παρακάτω δίνω το παράδειγμα της HelloAge κλάσης που είναι υποκλάση της γνωστής μας Hello. Οι υποκλάσεις κληρονομούν ιδιότητες και συναρτήσεις από την μητέρα υπερκλάση.

HelloAge class

Στην συνέχεια δημιουργώ μιά κλάση που κάνει την δουλεία του προηγουμένου προγράμματος + κάτι επιπλέον. Συγκεκριμένα, εκτός του ονόματος, που δια χειρίζεται η μητέρα κλάση η HelloAge: α) ζητά από τον χρήστη την ηλικία του β) τυπώνει μήνυμα με κάποια στοιχεία σχετικά με την ηλικία του.
Για ευκολία παραθέτω και τον κώδικα της παλιάς κλάσης Hello.

class  Hello
{
private:
   char name[200];
   bool finish;
public:
   Hello() {finish = false;}
   virtual ~Hello(){}
   bool GetFinishValue() {return finish;}
   void GetNameFromUser();
   void PrintMessage();
   void AskFinish();
};

class  HelloAge : public  Hello
{
private:
	int  age;    
public:
	HelloAge(){age=0;}
	virtual ~HelloAge(){}
	void  GetAgeFromUser();
	void  PrintAgeMessage();
};



Συστατικά της κλάσης

class HelloAge : public Hello Δήλωση της κλάσης HelloAge ως υποκλάσης της Hello. Το public σημαίνει ότι η HelloAge Είναι μιά κλάση Hello αλλά και κάτι επιπλέον.
int age; Η μία και μοναδική μεταβλητή της κλάσης, ένας ακέραιος όπου θα τοποθετηθεί η ηλικία του χρήστη. Η υποκλάση δεν έχει πρόσβαση στις μεταβλητές της μητέρας κλάσης (Hello) διότι αυτές έχουν ορισθεί ως private.
HelloAge(){age=0;} Ο κύριος κατασκευαστής (constructor) της κλάσης, που κατά την δημιουργία της θέτει την μεταβλητή age=0, σε πιό πολύπλοκες κλάσεις μπορεί να γίνονται εδώ κάποιες προετοιμασίες γιά την χρήση της κλάσσης. Σημείωσε ότι κατά την δημιουργία της κλάσης καλήται πρώτα ο κατασκευαστής της μητέρας κλάσης και κατόπιν αυτός της υποκλάσης, εφόσον και οι δύο είναι public.
virtual ~HelloAge(){} Ο καταστροφέας (destructor) της κλάσης, που εδώ δεν κάνει τίποτε, αλλά σε πιό σύνθετες κλάσεις συνήθως απελευθερώνει μνήμη που χρησιμοποιεί η κλάση. Σημείωσε ότι πρώτα καλήται αυτός ο καταστροφέας και κατόπιν εκείνος της μητέρας κλάσης. Δηλαδή η σειρά κλήσης των καταστροφέων είναι αντίθετη της σειράς κλήσης των κατασκευαστών.
void GetAgeFromUser(); Μιά συνάρτηση η οποία θα ζητά από τον χρήστη την ηλικία του και θα την αποθηκεύει στην μεταβλητή age (κατασκευάζεται το σώμα της παρακάτω)
void PrintAgeMessage(); Μιά συνάρτηση που τυπώνει κάποιο μήνυμα (κατασκευάζεται το σώμα της παρακάτω)

Τα παραπάνω μπλοκ αναγνωρίζονται από την C++ σαν ορισμοί μιάς κλάσης και μιάς υποκλάσης. Οι public συναρτήσεις της μητέρας μπορούν να κληθούν από την υποκλάση. Το δεύτερο μπλοκ είναι το interface της υποκλάσης. Περιέχει όλη την πληροφορία γιά το τι κάνει η κλάση. Το πως το κάνει αυτό που κάνει καθορίζεται από το σώμα των διαφόρων συναρτήσεων οι οποίες συνολικά αποτελούν (με τα σώματά τους) την implementation (υλοποίηση) της κλάσης. Στην συγκεκριμένη περίπτωση η υλοποίηση (της παλιάς και της νέας κλάσης) είναι η εξής:

Υλοποίηση της κλάσης(implementation)

void Hello::GetNameFromUser()
{
   cout << "Write your name:\n" << endl;
   cin >> name ;
}

void Hello::PrintMessage()
{
   cout <<"\n ********* Hello " << name << 
		 " Welcome to C++  ********\n\n" << endl;
}

void Hello::AskFinish()
{
	char  response[10];
	cout << "Finish?...Yes(Y) ... No(N)....\n" << endl;
	cin  >> response ;
	finish = (response[0]=='Y' || response[0]=='y');
}

Υλοποίηση της κλάσης HelloAge

void HelloAge::GetAgeFromUser()
{
	cout  << "How old are you?\n" << endl ;
	cin >> age ;
}

void HelloAge::PrintAgeMessage()
{
	int  weeks = age*48;
	int  months = age*12;
	int  days = age*365;
	int  hours = days*24;

	cout << "You live " <<  age  <<  " years \n" 
		 << months << " months at least,\n "
		 << weeks << " weeks at least,\n "
		 << days <<" days at least \n"
		 << hours << " hours at least!"  << endl;
}
Βλέπουμε ότι στην υλοποίηση ορίζεται το σώμα των συναρτήσεων (member functions) της κλάσης. Η C++ αναγνωρίζει ότι πρόκειται για μέλη της κλάσης HelloAge από την προσθήκη του HelloAge:: μπροστά από κάθε συνάρτηση.

Οργάνωση του κώδικα μιας κλάσης

Όπως είδαμε προηγουμένως, ο κώδικας που ορίζει μιά κλάση χωρίζεται φυσιολογικά σε δύο μπλοκ. Το πρώτο (interface) ορίζει τις ιδιότητες μιάς κλάσης. Αν ο προγραμματιστής επιλέξει κατάλληλα ονόματα γιά τις συναρτήσεις, στο interface φαίνεται TI ακριβώς κάνει η κλάση. Το ΠΩΣ το κάνει φαίνεται στο άλλο μπλόκ, αυτό της implementation. Την εννοιολογική αυτήν διαίρεση ακολουθούν, κατά κανόνα, οι προγραμματιστές γιά να διαιρέσουν τον κώδικα και να τον τοποθετήσουν σε διαφορετικά αρχεία. Το interface της κλάσης τοποθετήται σε ένα αρχείο με την κατάλληξη .h, ενώ η υλοποίηση (implementation) τοποθετήται σε ένα αρχείο με την κατάλληξη .cpp. Παρόλο που η παρούσα υποκλάση είναι μικρή και τετριμμένη, ακολουθώ αυτόν τον κανόνα οργάνωσης και δημιοργώ δύο ξεχωριστά αρχεία και γιά την κλάση HelloAge.

Interface, δημιουργούμε αρχείο κειμένου με όνομα HelloClass.h το οποίο περιέχει:

#ifndef  HELLOCLASS
#define  HELLOCLASS

class  Hello
{
private:
   char name[200];
   bool finish;
public:
   Hello() {finish = false;}
   virtual ~Hello(){}
   bool GetFinishValue() {return finish;}
   void GetNameFromUser();
   void PrintMessage();
   void AskFinish();
}; 

#endif // HELLOCLASS

Δημιουργούμε επίσης αρχείο κειμένου με όνομα HelloAgeClass.h το οποίο περιέχει τον κώδικα.

#ifndef  HELLOAGECLASS
#define  HELLOAGECLASS

#include "Hello.h"   

class  HelloAge : public  Hello
{
private:
	int  age;    
public:
	HelloAge(){age=0;}
	virtual ~HelloAge(){}
	void  GetAgeFromUser();
	void  PrintAgeMessage();
};

#endif // HELLOAGECLASS
Σημείωσε την εντολή συμπερίληψης
 #include "Hello.h" 
στην αρχή του αρχείου. Αυτή εξασφαλίζει την διασύνδεση με την μητέρα κλάση. Αν δεν βάζαμε αυτή την εντολή, ο μεταφραστής θα έδινε μήνυμα λάθους, αναφέροντάς μας ότι δεν του είναι γνωστή καμμία κλάση Hello.

Impementation, δημιουργούμε αρχείο κειμένου με όνομα HelloClass.cpp το οποίο περιέχει:

#include <iostream.h>
#include <HelloClass.h>

void Hello::GetNameFromUser()
{
   cout << "Write your name:\n" << endl;
   cin >> name ;
}

void Hello::PrintMessage()
{
   cout <<"\n ********* Hello " << name << 
		 " Welcome to C++  ********\n\n" << endl;
}

void Hello::AskFinish()
{
	char  response[10];
	cout << "Finish?...Yes(Y) ... No(N)....\n" << endl;
	cin  >> response ;
	finish = (response[0]=='Y' || response[0]=='y');
}
Δημιουργούμε επίσης αρχείο κειμένου με όνομα HelloAgeClass.cpp το οποίο περιέχει:
#include <iostream.h>
#include <HelloAgeClass.h>

void HelloAge::GetAgeFromUser()
{
	cout  << "How old are you?\n" << endl ;
	cin >> age ;
}

void HelloAge::PrintAgeMessage()
{
	int  weeks = age*48;
	int  months = age*12;
	int  days = age*365;
	int  hours = days*24;

	cout << "You live " <<  age  <<  " years \n" 
		 << months << " months at least,\n "
		 << weeks << " weeks at least,\n "
		 << days <<" days at least \n"
		 << hours << " hours at least!"  << endl;
}

Παλιό πρόγραμμα (Hello) με κλάσεις/υποκλάσεις

Σ΄ένα αρχείο με όνομα Hello.cpp γράφω τον κώδικα:
#include <stdio.h>
#include "HelloClass.h"
#include "HelloAgeClass.h"

int main(int argc, char* argv[])
{
	HelloAge  h ;
	while(h.GetFinishValue()==false)
	{
		 h.GetNameFromUser();
		 h.PrintMessage();
		 h.GetAgeFromUser();
		 h.PrintAgeMessage();
		 h.AskFinish();
	}
	return 0;
}

Ανάλυση του κώδικα

#include <stdio.h> Εντολή συμπερίληψης βασικής βιβλιοθήκης της C++
#include "HelloClass.h" #include "HelloAgeClass.h" Εντολή συμπερίληψης των αρχείων interface της κλάσης/υποκλάσης που θα χρησιμοποιήσω
HelloAge h; Ορισμός μιάς μεταβλητής (h) τύπου HelloAge. Η h λέγεται και στιγμιότυπο (instance) της κλάσης HelloAge. Σημείωσε ότι αμέσως μετά τον ορισμό ο compiler εκτελεί τον κατασκευαστή (constructor) της Hello (μητέρας κλάσης), σύμφωνα με τον οποίο (δες πιο πάνω) η τιμή της finish ορίζεται false. Κατόπιν εκτελεί τον κατασκευαστή της HelloAge, σύμφωνα με τον οποίο η τιμή της age ορίζεται ίση με 0.
while(h.GetFinishValue()==false) Μιά σύνθετη εντολή. Η εντολή while στέκεται στην κεφαλή του βρόγχου και εξετάζει την τιμή της μεταβλητής finish της κλάσης. Την τιμή αυτή την παίρνουμε με την συνάρτηση h.GetFinishValue() και την συγκρίνουμε με την false. Ο βρόγχος συνεχίζει να λειτουργεί εφ΄όσον η τιμή της finish είναι false. Σημείωσε ότι η h παρόλο που είναι μεταβλητή τύπου HelloAge χρησιμοποίεί αυτήν την συνάρτηση της μητέρας κλάσης Hello. Το ίδιο συμβαίνει και με τις άλλες public συναρτήσεις της Hello παρακάτω.
h.GetNameFromUser(); Η συνάρτηση που παίρνει από τον χρήστη το όνομα και το αποθηκεύει στην μεταβλητή name
h.PrintMessage(); Η συνάρτηση που τυπώνει ένα μήνυμα και το όνομα που έδωσε ο χρήστης
h.GetAgeFromUser(); Η συνάρτηση που παίρνει από τον χρήστη την ηλικία και την αποθηκεύει στην μεταβλητή age. Τούτη είναι συνάρτηση της HelloAge και όχι της μητέρας κλάσης Hello. Αν είχαμε ανακοινώση την μεταβλητή h με την εντολή
Hello  h ; 
αντί της
HelloAge  h ;
, θα είχαμε κάνει σφάλμα και κατά την εκτέλεση της
 h.GetAgeFromUser();
ο μεταφραστής θα έβγαζε μήνυμα λάθους, δηλώνοντας ότι η κλάση Hello δεν έχει καμμία συνάρτηση GetAgeFromUser.
h.PrintAgeMessage(); Η συνάρτηση που τυπώνει ένα μήνυμα σχετικό με την ηλικία που έδωσε ο χρήστης. Και εδώ ισχύει η παρατήρηση της προηγούμενης εντολής.
h.AskFinish(); Η συνάρτηση που ζητά από τον χρήστη αν θα συνεχίσει ή όχι, και ανάλογα με την απάντηση δίδει τιμή στην finish. Σημείωσε εδώ ότι αν η finish ήταν public, θα ήταν σωστή η εντολή
 if(h.finish==true) { ...αλλες εντλολές ... } 
Η τελεία h. Καθορίζει ότι οι συναρτήσεις που χρησιμοποιούμε είναι μέλη (member-functions) της κλάσης στην οποία ανήκει η μεταβλητή h

Εκτέλεση του προγράμματος

Ασκήσεις

1)   Κατασκευάστε μιά κλάση HelloShoe η οποία είναι υποκλάση της HelloAge και κάνει το εξής: α) ζητά το νούμερο παπουτσιού του χρήστη β) βγάζει ένα μήνυμα στο οποίο μεταξύ των άλλων περιέχεται και το άθροισμα της ηλικίας και το νούμερο παπουτσιού του. Κατασκευάστε και πρόγραμμα χρήσης της κλάσης.
2)   Κατασκευάστε μιά κλάση HelloHeight η οποία είναι υποκλάση της HelloShoe και κάνει το εξής: α) ζητά το ύψος του χρήστη και β) βγάζει μήνυμα στο οποίο, μεταξύ άλλων, περιέχεται και το πηλίκον (ηλικίας+νούμερο παπ.)/ύψος. Κατασκευάστε και πρόγραμμα χρήσης της κλάσης.
3)   Κατασκευάστε μιά νέα κλάση HelloNew η οποία κάνει από μόνη της όλες τις δουλειές που κάνουν οι τέσσερις κλάσεις Hello, HelloAge, HelloShoe, HelloHeight. Κατασκευάστε και πρόγραμμα χρήσης αυτής της κλάσης.