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

Υποστηρίζεται ότι η υπερφόρτωση τελεστών είναι χρήσιμη επειδή επιτρέπει στον προγραμματιστή να προγραμματίσει χρησιμοποιώντας σημειογραφία "πιο κοντά στο πεδίο του σκοπού" ("closer to the target domain")[1] και επιτρέπει σε τύπους που έχουν οριστεί από το χρήστη συντακτική υποστήριξη στο ίδιο επίπεδο με τους τύπους που είναι ενσωματωμένοι στη γλώσσα. Μπορεί εύκολα να προσομοιωθεί με τη χρήση κλήσεων συναρτήσεων. Για παράδειγμα, αν θεωρήσουμε τους ακεραίους a, b, c:

a + b * c

Σε μια γλώσσα που υποστηρίζει υπερφόρτωση τελεστών, και θεωρώντας ότι ο τελεστής '*' έχει υψηλότερη προτεραιότητα από τον τελεστή '+', η γραφή αυτή είναι ένας πιο συνοπτικός τρόπος από το να γράψουμε:

add (a, multiply (b,c))

Παραδείγματα Επεξεργασία

Σε αυτή την περίπτωση, ο τελεστής της πρόσθεσης υπερφορτώνεται ώστε να επιτρέπει την πρόσθεση ενός ορισμένου από το χρήστη τύπου "Time" (σε C++):

Time operator+(const Time& lhs, const Time& rhs) 
{
    Time temp = lhs;
    temp.seconds += rhs.seconds;
    if (temp.seconds >= 60) {
        temp.seconds -= 60;
        temp.minutes++;
    }
    temp.minutes += rhs.minutes;
    if (temp.minutes >= 60) {
        temp.minutes -= 60;
        temp.hours++;
    }
    temp.hours += rhs.hours;
    return temp;
}

Η πρόσθεση είναι μια δυαδική πράξη, το οποίο σημαίνει ότι έχει ένα δεξιό κι ένα αριστερό όρισμα. Στη C++, τα ορίσματα που περνάμε είναι τα ορίσματα της πράξης, και το αντικείμενο temp είναι η επιστρεφόμενη τιμή.

Η πράξη θα μπορούσε επίσης να οριστεί ως μέθοδος κάποιας κλάσης, αντικαθιστώντας το lhs με το κρυμμένο όρισμα this. Ωστόσο, αυτό εξαναγκάζει το αριστερό όρισμα να είναι τύπου Time και υποθέτει ότι το this είναι πιθανώς μια μεταβλητή lvalue:

Time Time::operator+(const Time& rhs) const 
{
    Time temp = *this;  /* Αντιγράφει το 'this' που δεν πρόκειται να μεταβληθεί */
    temp.seconds += rhs.seconds;
    if (temp.seconds >= 60) {
        temp.seconds -= 60;
        temp.minutes++;
    }
    temp.minutes += rhs.minutes;
    if (temp.minutes >= 60) {
        temp.minutes -= 60;
        temp.hours++;
    }
    temp.hours += rhs.hours;
    return temp;
}

Σημειώνεται ότι ένας μοναδιαίος τελεστής ορισμένος ως μέθοδος κάποιας κλάσης δε θα δεχόταν κανένα όρισμα (δουλεύει μόνο με το this):

bool Time::operator!() const 
{
    return ((hours == 0) && (minutes == 0) && (seconds == 0));
}

Κριτική Επεξεργασία

Η υπερφόρτωση τελεστών συχνά δέχεται κριτική επειδή επιτρέπει στους προγραμματιστές να δίνουν στους τελεστές εντελώς διαφορετική σημασία αναλόγως του τύπου των ορισμάτων. Για παράδειγμα, η χρήση του << στον παρακάτω κώδικα C++:

a << 1

Ολισθαίνει τα bits της μεταβλητής a αριστερά κατά 1 bit εφόσον η μεταβλητή a είναι ακεραίου τύπου, αλλά αν η μεταβλητή a είναι ροή εξόδου (output stream) τότε η παραπάνω εντολή θα προσπαθήσει να γράψει "1" στη ροή. Επειδή η υπερφόρτωση υποτύπων επιτρέπει στον αρχικό προγραμματιστή να αλλάξει τη συνήθη σημασία ενός τελεστή και να φέρει προ εκπλήξεων μελλοντικούς προγραμματιστές, είθισται να θεωρείται καλή πρακτική η χρήση υπερφόρτωσης τελεστών με προσοχή.

Η συνήθης απάντηση στην κριτική αυτή είναι ότι το ίδιο ισχύει και για την υπερφόρτωση συναρτήσεων. Επιπλέον, ακόμα και χωρίς την ύπαρξη υπερφόρτωσης, ο προγραμματιστής μπορεί να ορίσει μια συνάρτηση για να κάνει κάτι διαφορετικό από αυτό που υποδεικνύει το όνομά της. Ένα θέμα που παραμένει είναι ότι γλώσσες όπως η C++ προσφέρουν ένα περιορισμένο σύνολο συμβόλων-τελεστών, αφαιρώντας έτσι από τους προγραμματιστές την επιλογή κάποιου πιο ταιριαστού συμβόλου για μια νέα πράξη.

Ένα άλλο, πιο λεπτό θέμα με τους τελεστές είναι ότι συγκεκριμένοι κανόνες από τα μαθηματικά μπορεί αναμένονται, ή (εσφαλμένα) να θεωρούνται δεδομένοι. Για παράδειγμα, η αντιμεταθετικότητα του + (π.χ. ότι a + b == b + a) δεν ισχύει πάντα. Ένα τέτοιο παράδειγμα είναι όταν τα ορίσματα είναι συμβολοσειρές, εφόσον το + συχνά είναι υπερφορτωμένο ώστε να εκτελεί τη συνένωση συμβολοσειρών (π.χ. το "school" + "bag" έχει ως αποτέλεσμα τη συμβολοσειρά "schoolbag", η οποία είναι διαφορετική από το "bag" + "school" που έχει ως αποτέλεσμα τη συμβολοσειρά "bagschool"). Μια χαρακτηριστική απάντηση σε αυτό το επιχείρημα έρχεται από τα μαθηματικά: ενώ το + είναι αντιμεταθετικό στους ακεραίους (και εν γένει σε όλους τους πραγματικούς αριθμούς), δεν είναι αντιμεταθετικό για άλλους "τύπους" μεταβλητών. Επιπλέον, μπορεί να σημειωθεί ότι το + δεν είναι αντιμεταθετικό ούτε για πραγματικούς αριθμούς στην πράξη λόγω λαθών στρογγυλοποίησης. Ένα άλλο παράδειγμα: η δυαδική πράξη * (πολλαπλασιασμός) είναι αντιμεταθετική για ακέραιους αλλά όχι για πίνακες πολλαπλασιασμός πινάκων.

Κατάλογος Επεξεργασία

Μια ταξινόμηση κάποιων εκ των κοινών γλωσσών προγραμματισμού ως προς το εάν οι τελεστές τους είναι υπερφορτώσιμοι από τον προγραμματιστή ή είναι περιορισμένοι σε ένα προκαθορισμένο σύνολο.

Τελεστές Μη υπερφορτώσιμες Υπερφορτώσιμες
Ορισμός νέων
Περιορισμένο σύνολο

Ιστορία της υπερφόρτωσης τελεστών Επεξεργασία

1960s Επεξεργασία

Η προδιαγραφή της ALGOL 68 επέτρεπε την υπερφόρτωση τελεστών.[7]

Απόσπασμα από την προδιαγραφή της γλώσσας ALGOL 68 (σελίδα 177) όπου ορίζονται οι υπερφορτωμένοι τελεστές ¬, =, ≠ και abs:

10.2.2. Operations on Boolean Operands
a) op ∨ = (bool a, b) bool:( a | true | b );
b) op ∧ = (bool a, b) bool: ( a | b | false );
c) op ¬ = (bool a) bool: ( a | false | true );
d) op = = (bool a, b) bool:( a∧b ) ∨ ( ¬b∧¬a );
e) op ≠ = (bool a, b) bool: ¬(a=b);
f) op abs = (bool a)int: ( a | 1 | 0 );

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

1980s Επεξεργασία

Η Ada υποστηρίζει υπερφόρτωση τελεστών εξ αρχής, με την έκδοση του προτύπου της γλώσσας Ada 83. Ωστόσο, οι σχεδιαστές της γλώσσας επέλεξαν να μην επιτρέψουν τον ορισμό νέων τελεστών: μόνο οι υπάρχοντες τελεστές της γλώσσας μπορούν να υπερφορτωθούν (με τον ορισμό νέων συναρτήσεων με αναγνωριστικά όπως τα "+", "*", "and", κλπ.). Οι μετέπειτα αναθεωρήσεις της γλώσσας (το 1995 και το 2005) διατηρούν τον περιορισμό στην υπερφόρτωση υπάρχοντων τελεστών.

Η υπερφόρτωση τελεστών στη C++ είναι ακόμα πιο λεπτή από αυτή της ALGOL 68.[8]

1990s Επεξεργασία

Η Sun επιλέγει να μην περιλάβει την υπερφόρτωση τελεστών στη γλώσσα Java.[9][10]

Η γλώσσα προγραμματισμού Ruby επιτρέπει την υπερφόρτωση τελεστών ως συντακτική ζάχαρη (syntactic sugar) για απλές κλήσεις μεθόδων.

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

2000's Επεξεργασία

Η Microsoft περιλαμβάνει την υπερφόρτωση τελεστών στη C# το 2001.

Η γλώσσα προγραμματισμού Scala επιτρέπει την υπερφόρτωση τελεστών μέσω της πολλαπλής αποστολής.

Δείτε Επίσης Επεξεργασία

Αναφορές Επεξεργασία

  1. «C++ FAQ Lite: What are the benefits of operator overloading?». Ιούνιος 2010. Αρχειοθετήθηκε από το πρωτότυπο στις 14 Αυγούστου 2011. Ανακτήθηκε στις 1 Αυγούστου 2010. 
  2. οι δυαδικές συναρτήσεις με συμβολικό όνομα μπορούν να καλούνται τοποθετώντας τις ανάμεσα στα ορίσματά τους (infix)
  3. εμφανίστηκε στην Fortran 90
  4. type classes αντί για υπερφόρτωση
  5. «Why does Go not support overloading of methods and operators?». Ανακτήθηκε στις 4 Σεπτεμβρίου 2011. 
  6. «Operator Overloading, Free Pascal Manual». Αρχειοθετήθηκε από το πρωτότυπο στις 23 Ιουνίου 2011. Ανακτήθηκε στις 30 Ιουνίου 2011. 
  7. Barry J. Mailloux· John E. L. Peck· Cornelis H.A. Koster (Αύγουστος 1968). A. van Wijngaarden, επιμ. «Report on the Algorithmic Language ALGOL 68, Section 10.2.2» (PDF). Αρχειοθετήθηκε από το πρωτότυπο (PDF) στις 17 Ιουλίου 2012. Ανακτήθηκε στις 1 Απριλίου 2007. 
  8. Bjarne Stroustrup. «A History of C++: 1979−1991 - page 12» (PDF). Αρχειοθετήθηκε (PDF) από το πρωτότυπο στις 10 Δεκεμβρίου 2006. Ανακτήθηκε στις 30 Απριλίου 2007. 
  9. [1]
  10. [2]