Υπερφόρτωση τελεστών

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

Υποστηρίζεται ότι η υπερφόρτωση τελεστών είναι χρήσιμη επειδή επιτρέπει στον προγραμματιστή να προγραμματίσει χρησιμοποιώντας σημειογραφία "πιο κοντά στο πεδίο του σκοπού" ("closer to the target domain") και επιτρέπει σε τύπους που έχουν οριστεί από το χρήστη συντακτική υποστήριξη στο ίδιο επίπεδο με τους τύπους που είναι ενσωματωμένοι στη γλώσσα. Μπορεί εύκολα να προσομοιωθεί με τη χρήση κλήσεων συναρτήσεων. Για παράδειγμα, αν θεωρήσουμε τους ακεραίους 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"). Μια χαρακτηριστική απάντηση σε αυτό το επιχείρημα έρχεται από τα μαθηματικά: ενώ το + είναι αντιμεταθετικό στους ακεραίους (και εν γένει σε όλους τους πραγματικούς αριθμούς), δεν είναι αντιμεταθετικό για άλλους "τύπους" μεταβλητών. Επιπλέον, μπορεί να σημειωθεί ότι το + δεν είναι αντιμεταθετικό ούτε για πραγματικούς αριθμούς στην πράξη λόγω λαθών στρογγυλοποίησης. Ένα άλλο παράδειγμα: η δυαδική πράξη * (πολλαπλασιασμός) είναι αντιμεταθετική για ακέραιους αλλά όχι για πίνακες πολλαπλασιασμός πινάκων.

 Κατάλογος 
Μια ταξινόμηση κάποιων εκ των κοινών γλωσσών προγραμματισμού ως προς το εάν οι τελεστές τους είναι υπερφορτώσιμοι από τον προγραμματιστή ή είναι περιορισμένοι σε ένα προκαθορισμένο σύνολο.
 Τελεστές Μη υπερφορτώσιμες Υπερφορτώσιμες Ορισμός νέων 
 ML
 Picoοι δυαδικές συναρτήσεις με συμβολικό όνομα μπορούν να καλούνται τοποθετώντας τις ανάμεσα στα ορίσματά τους (infix)
 Prolog ALGOL 68
 Eiffel
 Fortranεμφανίστηκε στην Fortran 90
 F#
 Haskelltype classes αντί για υπερφόρτωση
 Io
 Perl 6
 R
 Scala
 Smalltalk Περιορισμένο σύνολο 
 C
 Go 
 Java
 JavaScript
 Modula-2
 Objective-C
 Pascal
 BASIC Ada
 C++
 C#
 D
 FreeBASIC
 Groovy
 Lua
 Delphi (από το 2005)
 Object Pascal (Free Pascal)
 PHP [χρησιμοποιώντας "μαγικές μεθόδους" ή διεπαφή ArrayAccess (ArrayAccess interface) ή επέκταση τελεστή (Operator extension)]
 Perl
 Python
 Ruby
 Visual Basic 2005

 Ιστορία της υπερφόρτωσης τελεστών 
 1960s 
Η προδιαγραφή της ALGOL 68 επέτρεπε την υπερφόρτωση τελεστών.

Απόσπασμα από την προδιαγραφή της γλώσσας 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.

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

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

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

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

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

 Δείτε Επίσης 
 Υπερφόρτωση συναρτήσεων
 Πολυμορφισμός (επιστήμη υπολογιστών)
 Υπορουτίνα
 Τελεστές (προγραμματισμός)
 Τελεστές στη C και τη C++

 Αναφορές 

Κατηγορία:Δομές προγραμματισμού