User Tools

Site Tools


cpp:classes

Κλάσεις και Αντικείμενα

Όπως και στη Java οι κλάσεις στη C++ δημιουργούν νέους τύπους δεδομένων. Παρακάτω δίνεται το παράδειγμα της κλάσης Rectangle με παράλληλη δημιουργία ενός αντικειμένου της κλάσης αυτής.

Rectangle.cpp
#include <iostream>
using namespace std;
 
class Rectangle {
  private:
    int width, height;
  public:
    void setWidth(int w);
    void setHeight(int h);
    int getWidth() const;
    int getHeight() const;
};
 
void Rectangle::setWidth(int w) { width = w; }
void Rectangle::setHeight(int h) { height = h; }
int Rectangle::getWidth() const { return width; }
int Rectangle::getHeight() const { return height; }
 
int main () {
  Rectangle rect;
  rect.setWidth(5);
  rect.setHeight(6);
  cout << "area: " << rect.getWidth() * rect.getHeight() << endl;
  return 0;
}

Η παραπάνω κλάση διαθέτει τα πεδία τύπου int width, height και τις μεθόδους setWidth, setHeight, getWidth, getHeight. Η κλάση δεν διαθέτει κατασκευαστή. Η δήλωση Rect rect; στη μέθοδο main() δημιουργεί ένα αντικείμενο με κλήση του default κατασκευαστή, ο οποίος δημιουργείται από τον compiler, λόγω της έλλειψης κατασκευαστή στον κώδικα.

Διάκριση μεταξύ δήλωσης της κλάσης και υλοποίησης της κλάσης

Στο προηγούμενο παράδειγμα, η δήλωση της κλάσης και η υλοποίηση των μεθόδων (συναρτήσεων) της κλάσης βρίσκονται στο ίδιο αρχείο. Η δήλωση στο ίδιο αρχείο της κλάσης και των μεθόδων της δεν είναι συνήθης πρακτική. Αντ' αυτού, η δήλωση της κλάσης δηλώνεται σε ένα header file με το όνομα της κλάσης και κατάληξη .hpp ή .h, ενώ η υλοποίηση των μεθόδων σε ένα αρχείο .cpp με το ίδιο όνομα. Η κλάση Rectangle που αναφέρεται παραπάνω θα πρέπει να χωριστεί σε δύο αρχεία ως εξής:

Rectangle.hpp
#include <iostream>
using namespace std;
 
class Rectangle {
  private:
    int width, height;
  public:
    void setWidth(int w);
    void setHeight(int h);
    int getWidth() const;
    int getHeight() const;
};
Rectangle.cpp
#include "Rectangle.hpp"
 
void Rectangle::setWidth(int w) { width = w; }
void Rectangle::setHeight(int h) { height = h; }
int Rectangle::getWidth() const { return width; }
int Rectangle::getHeight() const { return height; }

Ο λόγος που διακρίνουμε μία κλάση σε δήλωση και υλοποίηση είναι ότι η δήλωση της κλάσης πιθανόν να συμπεριληφθεί μέσω μιας εντολής #include από περισσότερα του ενός αρχεία στο ίδιο πρόγραμμα. Η υλοποίηση όμως δεν θέλουμε να συμπεριληφθεί σε περισσότερα του ενός μεταγλωττισμένα αρχεία, διότι ο linker στη συνέχεια θα διαμαρτύρεται ότι η ίδια συνάρτηση υπάρχει περισσότερες από μία φορές μέσα στο τελικό πρόγραμμα. Επιπλέον, δεν είναι καλή πρακτική στον αντικειμενοστραφή προγραμματισμό να αποκαλύπτουμε στον προγραμματιστή-χρήστη της κλάσης της εσωτερική υλοποίηση της. Έτσι, μπορούμε να διατηρήσουμε “κρυφή” την υλοποίηση των μεθόδων σε ξεχωριστό αρχείο από τη δήλωση της κλάσης.

Στη συνέχεια, για λόγους οικονομίας χώρου και ευκολότερης μεταγλώττισης του κώδικα πολλά παραδείγματα θα εμφανίζουν τη δήλωση της κλάσης και την υλοποίηση των μεθόδων της στο ίδιο αρχείο. Πρέπει να έχετε υπόψη σας ότι αυτό αν και δεν είναι λάθος, μπορεί να εφαρμοστεί μόνο σε πολύ απλά και περιορισμένου εύρους προγράμματα.

Οι μέθοδοι της κλάσης

Οι μέθοδοι της κλάσης είναι συναρτήσεις οι οποίες έχουν απευθείας πρόσβαση στα πεδία (μεταβλητές) της κλάσης. Οι μέθοδοι ορίζονται μέσα στην κλάση ή ορίζεται το πρότυπο τους μέσα στην κλάση και η υλοποίηση τους εκτός. Από το παρακάτω παράδειγμα παρατηρήστε τους δύο διαφορετικούς τρόπους ορισμού των μεθόδων setWidth και setHeight. H υλοποίηση της setHeight ορίζεται μέσα στη δήλωση της κλάσης, ενώ η υλοποιηση της setWidth έξω από την κλάση.

Rectangle.hpp
#include <iostream>
using namespace std;
 
class Rectangle {
  private:
    int width, height;
  public:
    int getWidth() const;
    int getHeight() const;
    void setWidth(int w);
    void setHeight(int h) { height = h; }
};
Rectangle.cpp
#include "Rectangle.hpp"
 
int Rectangle::getWidth() const { return width;}
int Rectangle::getHeight() const { return height;}
void Rectangle::setWidth(int w) { width = w; }

Οι δύο παραπάνω τρόποι ορισμού μιας μεθόδου της κλάσης είναι ισοδύναμοι με την διαφορά ότι η μέθοδος setHeight ορίζεται ως inline ακόμη και εάν η λέξη inline δεν αναφέρεται ρητά. Ο λόγος που η setHeight γίνεται inline είναι για να αποφευχθεί το πρόβλημα των περισσότερων του ενός ορισμών της ίδιας μεθόδου σε επιμέρους μεταγλωττισμένα αρχεία του ιδίου προγράμματος, μέσω της συμπερίληψης (#include) του αρχείου .hpp από περισσότερα του ενός αρχεία .cpp στο ίδιο πρόγραμμα. Ο compiler προλαμβάνει το συγκεκριμένο πρόβλημα κάνοντας την μέθοδο inline.

Για τον ορισμό της μεθόδου setWidth εκτός της κλάσης είναι απαραίτητη η χρήση του ονόματος της κλάσης ακολουθούμενη από το scope operator Rectangle::setWidth(int w). Η χρήση του scope operator εξασφαλίζει ότι η μέθοδος ανήκει στην κλάση και δεν αποτελεί αυτόνομη συνάρτηση του προγράμματος.

Παρατηρήστε ότι οι μέθοδοι getWidth και getHeight έχουν το προσδιοριστή const αμέσως μετά τη δήλωση τους. Ο προσδιοριστής const σε αυτή την περίπτωση, δηλώνει ότι η συγκεκριμένη συνάρτηση δεν μεταβάλλει το αντικείμενο στο οποίο ανήκει. Η χρήση του προσδιοριστή const αποτελεί α) προστασία προς τον προγραμματιστή που υλοποιεί τη μέθοδο, ώστε σε περίπτωση που προσπαθήσει να γράψει ένα πεδίο της κλάσης η μεταγλώττιση να αποτύχει εμφανίζοντας μήνυμα λάθους και β) δήλωση προς τους προγραμματιστές-χρήστες της κλάσης ότι η συγκεκριμένη συνάρτηση δεν θα μεταβάλλει με οποιονδήποτε τρόπο την κατάσταση του αντικειμένου.

Πεδία που περιγράφονται από κλάσεις

Εκτός από πεδία βασικού τύπου μπορούμε να έχουμε και πεδία κλάσεων των οποίων ο τύπος περιγράφεται από κλάσεις. Παράδειγμα μιας τέτοια κλάσης είναι το ορθογώνιο παραλληλεπίπεδο - κυβοειδές (κλάση Cuboid) το οποίο έχει ένα πεδίο τύπου Rectangle το οποίο ορίσαμε παραπάνω.

Cuboid.hpp
#include <iostream>
using namespace std;
 
#include "Rectangle.hpp"
 
class Cuboid {
  private:
    int length;
    Rectangle rect;
  public:
    void setRectangle(Rectangle r);
    Rectangle getRectangle() const;
    void setLength(int l);
    int getLength() const;
    int volume();
};
Cuboid.cpp
#include "Cuboid.hpp"
 
void Cuboid::setRectangle(Rectangle r) {rect = r;}
Rectangle Cuboid::getRectangle() const {return rect;}
void Cuboid::setLength(int l) { length = l; }
int Cuboid::getLength() const { return length; }
 
int Cuboid::volume() {
  return length * rect.getWidth() * rect.getHeight();
}
cpp/classes.txt · Last modified: 2021/05/06 23:01 (external edit)