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

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

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

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

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++

Αναφορές 

Δομές προγραμματισμού