Υπερφόρτωση δυαδικών τελεστών γ. Βασικά στοιχεία υπερφόρτωσης χειριστή. Χειριστής τοποθέτησης νέος () και διαγραφή τελεστή ()

Βασικά στοιχεία υπερφόρτωσης χειριστή

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

// Λειτουργία + με ακέραιους αριθμούς. int a = 100; int b = 240; int c = a + b; // το c είναι τώρα 340

Δεν υπάρχει τίποτα νέο εδώ, αλλά έχετε σκεφτεί ποτέ ότι η ίδια λειτουργία + μπορεί να εφαρμοστεί στους περισσότερους από τους ενσωματωμένους τύπους δεδομένων στο C #; Για παράδειγμα, εξετάστε τον κώδικα όπως αυτός:

// Λειτουργία + με χορδές. string si = "Γεια"; string s2 = "world!"; συμβολοσειρά s3 = si + s2; // Το s3 περιέχει τώρα το "Hello world!"

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

Η γλώσσα C # παρέχει τη δυνατότητα δημιουργίας ειδικών κλάσεων και δομών που επίσης ανταποκρίνονται μοναδικά στο ίδιο σύνολο βασικών διακριτικών (όπως η λειτουργία +). Λάβετε υπόψη ότι απολύτως κάθε ενσωματωμένη λειτουργία C # δεν μπορεί να υπερφορτωθεί. Ο παρακάτω πίνακας περιγράφει τις δυνατότητες υπερφόρτωσης βασικών λειτουργιών:

Λειτουργία C # Δυνατότητα υπερφόρτωσης
+, -,!, ++, -, αληθές, ψευδές Αυτό το σύνολο μοναδικών λειτουργιών μπορεί να υπερφορτωθεί
+, -, *, /, %, &, |, ^, > Αυτές οι δυαδικές λειτουργίες μπορούν να υπερφορτωθούν
==, !=, <, >, <=, >= Αυτές οι λειτουργίες σύγκρισης μπορεί να υπερφορτωθούν. Το C # απαιτεί υπερφόρτωση λειτουργιών "όπως" μαζί (δηλ.< и >, <= и >=, == και! =)
Η λειτουργία δεν μπορεί να υπερφορτωθεί. Ωστόσο, οι δείκτες προσφέρουν παρόμοια λειτουργικότητα.
() Η λειτουργία () δεν μπορεί να υπερφορτωθεί. Ωστόσο, ειδικές μέθοδοι μετατροπής παρέχουν την ίδια λειτουργικότητα.
+=, -=, *=, /=, %=, &=, |=, ^=, >= Οι συντομευμένες εργασίες δεν μπορούν να υπερφορτωθούν. Ωστόσο, τα λαμβάνετε αυτόματα υπερφορτώνοντας την κατάλληλη δυαδική λειτουργία

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

// Γενική μορφή υπερφόρτωσης ενός τελεστή. δημόσιος στατικός τελεστής return_type op (παράμετρος_τύπος τελεστή) (// λειτουργίες) // Γενική μορφή υπερφόρτωσης δυαδικού τελεστή. δημόσιος στατικός τελεστής return_type op (parameter_type1 operand1, parameter_type2 operand2) (// λειτουργίες)

Εδώ, αντί για op, αντικαθίσταται ένας υπερφορτωμένος τελεστής, για παράδειγμα + ή /, και επιστροφή_τύπουυποδηλώνει τον συγκεκριμένο τύπο τιμής που επιστρέφεται από την καθορισμένη λειτουργία. Αυτή η τιμή μπορεί να είναι οποιουδήποτε τύπου, αλλά συχνά είναι του ίδιου τύπου με την κλάση για την οποία ο χειριστής υπερφορτώνεται. Αυτή η συσχέτιση διευκολύνει τη χρήση υπερφορτωμένων τελεστών σε εκφράσεις. Για ενιαίους χειριστές όρος πράξηςδηλώνει τον μεταδιδόμενο τελεστή και για δυαδικούς τελεστές δηλώνει το ίδιο τελεστής 1και τελεστής 2... Λάβετε υπόψη ότι οι μέθοδοι χειριστή πρέπει να έχουν προσδιοριστές κοινού και στατικού τύπου.

Υπερφόρτωση δυαδικών τελεστών

Ας δούμε την εφαρμογή της υπερφόρτωσης δυαδικού τελεστή με ένα απλό παράδειγμα:

Χρήση του συστήματος. χρησιμοποιώντας System.Collections.Generic; χρησιμοποιώντας System.Linq; χρησιμοποιώντας System.Text; namespace ConsoleApplication1 (κλάση MyArr (// Συντεταγμένες ενός σημείου σε τρισδιάστατο χώρο δημόσιο int x, y, z; δημόσιο MyArr (int x = 0, int y = 0, int z = 0) (this.x = x; this.y = y; this.z = z;) // Υπερφόρτωση του δυαδικού τελεστή + δημόσιος στατικός τελεστής MyArr + (MyArr obj1, MyArr obj2) (MyArr arr = νέο MyArr (); arr.x = obj1.x + obj2 .x; arr. y = obj1.y + obj2.y; arr.z = obj1.z + obj2.z; επιστροφή arr;) // Υπερφόρτωση του δυαδικού τελεστή - δημόσιος στατικός τελεστής MyArr - (MyArr obj1, MyArr obj2) (MyArr arr = νέο MyArr (); arr.x = obj1.x - obj2.x; arr.y = obj1.y - obj2.y; arr.z = obj1.z - obj2.z; επιστροφή arr;)) class Program (static void Main (args string) (MyArr Point1 = new MyArr (1, 12, -4); MyArr Point2 = new MyArr (0, -3, 18); Console.WriteLine ("Συντεταγμένες του πρώτου σημείου: " + Point1.x + "" + Point1.y + "" + Point1.z); Console.WriteLine ("Συντεταγμένες του δεύτερου σημείου:" + Point2.x + "" + Point2.y + "" + Point2. z + "\ n"); MyArr Point3 = Point1 + Point2; Console.WriteLine ("\ nPoint1 + Point2 =" + Point3.x + "" + Point3.y + "" + Point3.z); Point3 = Point1 - Point2; Console.WriteLine ("\ nPoint1 - Point2 =" + Point3.x + "" + Point3.y + "" + Point3.z); Console.ReadLine (); )))

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

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

Χρήση του συστήματος. χρησιμοποιώντας System.Collections.Generic; χρησιμοποιώντας System.Linq; χρησιμοποιώντας System.Text; namespace ConsoleApplication1 (κλάση MyArr (// Συντεταγμένες ενός σημείου σε τρισδιάστατο χώρο δημόσιο int x, y, z; δημόσιο MyArr (int x = 0, int y = 0, int z = 0) (this.x = x; this.y = y; this.z = z;) // Υπερφόρτωση του δυαδικού τελεστή + δημόσιος στατικός τελεστής MyArr + (MyArr obj1, MyArr obj2) (MyArr arr = νέο MyArr (); arr.x = obj1.x + obj2 .x; arr. y = obj1.y + obj2.y; arr.z = obj1.z + obj2.z; επιστροφή arr;) // Υπερφόρτωση του δυαδικού τελεστή - δημόσιος στατικός τελεστής MyArr - (MyArr obj1, MyArr obj2) (MyArr arr = νέο MyArr (); arr.x = obj1.x - obj2.x; arr.y = obj1.y - obj2.y; arr.z = obj1.z - obj2.z; επιστροφή arr;) / / Υπερφόρτωση του μοναδικού τελεστή - δημόσιος στατικός τελεστής MyArr - (MyArr obj1) (MyArr arr = νέο MyArr (); arr.x = -obj1.x; arr.y = -obj1.y; arr.z = -obj1.z ; return arr;) // Υπερφόρτωση του μοναδικού τελεστή ++ δημόσιο στατικό τελεστή MyArr ++ (MyArr obj1) (obj1.x + = 1; obj1.y + = 1; obj1.z + = 1; επιστροφή obj1;) / / Υπερφόρτωση του unary operator - publi c στατικός τελεστής MyArr - (MyArr obj1) (obj1.x - = 1; obj1.y - = 1; obj1.z - = 1; επιστροφή obj1; )) κλάση Πρόγραμμα (στατικό κενό Main (args συμβολοσειράς) (MyArr Point1 = νέο MyArr (1, 12, -4), MyArr Point2 = νέο MyArr (0, -3, 18), Console.WriteLine ("Οι συντεταγμένες του πρώτο σημείο: "+ Point1.x +" "+ Point1.y +" "+ Point1.z); Console.WriteLine (" Συντεταγμένες του δεύτερου σημείου: "+ Point2.x +" "+ Point2.y +" " + Point2.z + "\ n"); MyArr Point3 = Point1 + Point2; Console.WriteLine ("\ nPoint1 + Point2 =" + Point3.x + "" + Point3.y + "" + Point3.z); Point3 = Point1 - Point2; Console.WriteLine ("Point1 - Point2 =" + Point3.x + "" + Point3.y + "" + Point3.z); Point3 = -Point1; Console.WriteLine ("- Point1 =" + Point3.x + " "+ Point3.y +" "+ Point3.z); Point2 ++; Console.WriteLine (" Point2 ++ = "+ Point2.x +" "+ Point2.y +" "+ Point2. z); Point2--; Console. WriteLine ("Point2-- =" + Point2.x + "" + Point2.y + "" + Point2.z); Console.ReadLine ();)))

Τελευταία ενημέρωση: 12.08.2018

Μαζί με τις μεθόδους, μπορούμε επίσης να υπερφορτώσουμε τους τελεστές. Για παράδειγμα, ας υποθέσουμε ότι έχουμε την ακόλουθη κλάση Counter:

Μετρητής τάξης (δημόσια τιμή (λήψη, ορισμός;))

Αυτή η κλάση αντιπροσωπεύει κάποιο μετρητή, η τιμή του οποίου αποθηκεύεται στην ιδιότητα Value.

Και ας υποθέσουμε ότι έχουμε δύο αντικείμενα της κλάσης Counter - δύο μετρητές που θέλουμε να συγκρίνουμε ή να προσθέσουμε με βάση την ιδιότητά τους Value χρησιμοποιώντας τυπικές πράξεις σύγκρισης και πρόσθεσης:

Μετρητής c1 = νέος μετρητής (Τιμή = 23); Μετρητής c2 = νέος μετρητής (Τιμή = 45); bool αποτέλεσμα = c1> c2; Μετρητής c3 = c1 + c2;

Αλλά αυτή τη στιγμή, ούτε η λειτουργία σύγκρισης ούτε η λειτουργία πρόσθεσης είναι διαθέσιμες για αντικείμενα Counter. Αυτές οι λειτουργίες μπορούν να χρησιμοποιηθούν για έναν αριθμό πρωτόγονων τύπων. Για παράδειγμα, από προεπιλογή μπορούμε να προσθέσουμε αριθμητικές τιμές, αλλά ο μεταγλωττιστής δεν ξέρει πώς να προσθέσει αντικείμενα σύνθετων τύπων - κλάσεις και δομές. Και για αυτό πρέπει να υπερφορτώσουμε τους χειριστές που χρειαζόμαστε.

Η υπερφόρτωση τελεστή συνίσταται στον ορισμό μιας ειδικής μεθόδου στην κλάση για τα αντικείμενα της οποίας θέλουμε να ορίσουμε τον τελεστή:

Δημόσιος στατικός τελεστής τύπου return_type (παράμετροι) ()

Αυτή η μέθοδος πρέπει να έχει δημόσιους στατικούς τροποποιητές, καθώς ο υπερφορτωμένος τελεστής θα χρησιμοποιηθεί για όλα τα αντικείμενα αυτής της κλάσης. Ακολουθεί το όνομα του τύπου επιστροφής. Ο τύπος επιστροφής αντιπροσωπεύει τον τύπο από τον οποίο θέλουμε να πάρουμε τα αντικείμενα. Για παράδειγμα, ως αποτέλεσμα της προσθήκης δύο αντικειμένων Counter, αναμένουμε να λάβουμε ένα νέο αντικείμενο Counter. Και ως αποτέλεσμα της σύγκρισης των δύο, θέλουμε να πάρουμε ένα αντικείμενο τύπου bool, το οποίο υποδεικνύει εάν η υπό όρους έκφραση είναι true ή false. Αλλά ανάλογα με την εργασία, οι τύποι επιστροφής μπορεί να είναι οτιδήποτε.

Στη συνέχεια, αντί για το όνομα της μεθόδου, υπάρχει η λέξη-κλειδί τελεστή και ο ίδιος ο τελεστής. Και στη συνέχεια οι παράμετροι παρατίθενται σε αγκύλες. Οι δυαδικοί τελεστές λαμβάνουν δύο παραμέτρους, οι δυαδικοί τελεστές παίρνουν μία παράμετρο. Και σε κάθε περίπτωση, μία από τις παραμέτρους πρέπει να αντιπροσωπεύει τον τύπο - την κλάση ή τη δομή στην οποία ορίζεται ο τελεστής.

Για παράδειγμα, ας υπερφορτώσουμε έναν αριθμό τελεστών για την κλάση Counter:

Μετρητής κλάσης (δημόσια τιμή int (get; set;) δημόσιος στατικός τελεστής μετρητή + (Μετρητής c1, μετρητής c2) (επιστρέφει νέο μετρητή (Τιμή = c1.Τιμή + c2.Τιμή);) δημόσιος στατικός τελεστής bool> (Μετρητής c1, Μετρητής c2) (return c1.Value> c2.Value;) public static bool operator<(Counter c1, Counter c2) { return c1.Value < c2.Value; } }

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

Εφόσον στην περίπτωση της πράξης πρόσθεσης θέλουμε να προσθέσουμε δύο αντικείμενα της κλάσης Counter, ο τελεστής παίρνει δύο αντικείμενα αυτής της κλάσης. Και επειδή θέλουμε να λάβουμε ένα νέο αντικείμενο Counter ως αποτέλεσμα προσθήκης, αυτή η κλάση χρησιμοποιείται επίσης ως τύπος επιστροφής. Όλες οι ενέργειες αυτού του τελεστή περιορίζονται στη δημιουργία ενός νέου αντικειμένου, η ιδιότητα Value του οποίου συνδυάζει τις τιμές της ιδιότητας Value και των δύο παραμέτρων:

Δημόσιος στατικός τελεστής μετρητή + (Μετρητής c1, μετρητής c2) (επιστροφή νέου μετρητή (Τιμή = c1.Value + c2.Value);)

Δύο τελεστές σύγκρισης έχουν επίσης επανακαθοριστεί. Εάν παρακάμψουμε μία από αυτές τις πράξεις σύγκρισης, τότε πρέπει να παρακάμψουμε και τη δεύτερη από αυτές τις πράξεις. Οι ίδιοι οι τελεστές σύγκρισης συγκρίνουν τις τιμές των ιδιοτήτων Value και, ανάλογα με το αποτέλεσμα της σύγκρισης, επιστρέφουν είτε true είτε false.

Τώρα χρησιμοποιούμε υπερφορτωμένους τελεστές στο πρόγραμμα:

Στατικό κενό Κύριο (args συμβολοσειράς) (Μετρητής c1 = νέος μετρητής (Τιμή = 23); Μετρητής c2 = νέος μετρητής (Τιμή = 45), αποτέλεσμα bool = c1> c2; Console.WriteLine (αποτέλεσμα); // ψευδής Μετρητής c3 = c1 + c2; Console.WriteLine (c3.Value); // 23 + 45 = 68 Console.ReadKey ();)

Αξίζει να σημειωθεί ότι εφόσον, στην πραγματικότητα, ο ορισμός του τελεστή είναι μέθοδος, μπορούμε να υπερφορτώσουμε και αυτή τη μέθοδο, δηλαδή να δημιουργήσουμε μια άλλη έκδοση για αυτήν. Για παράδειγμα, ας προσθέσουμε έναν άλλο τελεστή στην κλάση Counter:

Δημόσιος στατικός τελεστής int + (Μετρητής c1, int val) (επιστροφή c1.Value + val;)

Αυτή η μέθοδος προσθέτει την τιμή της ιδιότητας Value και έναν αριθμό, επιστρέφοντας το άθροισμά τους. Και μπορούμε επίσης να χρησιμοποιήσουμε αυτόν τον τελεστή:

Μετρητής c1 = νέος μετρητής (Τιμή = 23); int d = c1 + 27; // 50 Console.WriteLine (d);

Θα πρέπει να ληφθεί υπόψη ότι κατά την υπερφόρτωση, τα αντικείμενα που μεταβιβάζονται στον χειριστή μέσω παραμέτρων δεν πρέπει να αλλάζουν. Για παράδειγμα, μπορούμε να ορίσουμε έναν τελεστή αύξησης για την κλάση Counter:

Δημόσιος στατικός τελεστής μετρητή ++ (Μετρητής c1) (c1.Value + = 10; επιστροφή c1;)

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

Και μια πιο σωστή υπερφόρτωση του τελεστή αύξησης θα μοιάζει με αυτό:

Δημόσιος στατικός τελεστής μετρητή ++ (Μετρητής c1) (επιστροφή νέου μετρητή (Τιμή = c1.Τιμή + 10);

Δηλαδή, επιστρέφεται ένα νέο αντικείμενο που περιέχει την αυξανόμενη τιμή στην ιδιότητα Value.

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

Για παράδειγμα, ας χρησιμοποιήσουμε τη λειτουργία αύξησης του προθέματος:

Μετρητής = νέος μετρητής () (Τιμή = 10); Console.WriteLine ($ "(counter.Value)"); // 10 Console.WriteLine ($ "((++ counter) .Value)"); // 20 Console.WriteLine ($ "(counter.Value)"); // είκοσι

Έξοδος κονσόλας:

Τώρα χρησιμοποιούμε την αύξηση postfix:

Μετρητής = νέος μετρητής () (Τιμή = 10); Console.WriteLine ($ "(counter.Value)"); // 10 Console.WriteLine ($ "((counter ++). Τιμή)"); // 10 Console.WriteLine ($ "(counter.Value)"); // είκοσι

Έξοδος κονσόλας:

Αξίζει επίσης να σημειωθεί ότι μπορούμε να παρακάμψουμε τους τελεστές true και false. Για παράδειγμα, ας τα ορίσουμε στην κλάση Counter:

Μετρητής κλάσης (δημόσιος int Τιμή (get; set;) δημόσιος στατικός τελεστής bool true (Μετρητής c1) (επιστροφή c1.Value! = 0;) δημόσιος στατικός τελεστής bool false (Μετρητής c1) (επιστροφή c1.Τιμή == 0;) // υπόλοιπο περιεχόμενο της τάξης)

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

Μετρητής = νέος μετρητής () (Τιμή = 0); if (counter) Console.WriteLine (true); else Console.WriteLine (false);

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

    μοναδικοί τελεστές +, -,!, ~, ++, -

    δυαδικοί τελεστές +, -, *, /,%

    πράξεις σύγκρισης ==,! =,<, >, <=, >=

    λογικοί τελεστές &&, ||

    τελεστές εκχώρησης + =, - =, * =, / =,% =

Και υπάρχει ένας αριθμός τελεστών που δεν μπορούν να υπερφορτωθούν, όπως ο τελεστής ισότητας = ή ο τριαδικός τελεστής ?:, καθώς και ορισμένοι άλλοι.

Μια πλήρης λίστα με υπερφορτωμένους τελεστές μπορεί να βρεθεί στην τεκμηρίωση msdn

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

Πολλές γλώσσες προγραμματισμού χρησιμοποιούν τελεστές: τουλάχιστον αναθέσεις (=,: =, ή παρόμοιους) και αριθμητικούς τελεστές (+, -, * και /). Στις περισσότερες στατικά πληκτρολογημένες γλώσσες, αυτοί οι τελεστές είναι δεσμευμένοι τύπου. Για παράδειγμα, στην Java, η προσθήκη με τον τελεστή + είναι δυνατή μόνο για ακέραιους αριθμούς, floats και συμβολοσειρές. Εάν ορίσουμε τις κλάσεις μας για μαθηματικά αντικείμενα, για παράδειγμα, για πίνακες, μπορούμε να εφαρμόσουμε μια μέθοδο για την πρόσθεσή τους, αλλά μπορείτε να την ονομάσετε κάπως έτσι: a = b.add (c).

Το C ++ δεν έχει αυτόν τον περιορισμό - μπορούμε να υπερφορτίσουμε σχεδόν οποιονδήποτε γνωστό τελεστή. Οι δυνατότητες είναι ατελείωτες: μπορείτε να επιλέξετε οποιονδήποτε συνδυασμό τύπων τελεστών, ο μόνος περιορισμός είναι η ανάγκη να υπάρχει τουλάχιστον ένας τελεστής που ορίζεται από το χρήστη. Δηλαδή, ορίστε έναν νέο τελεστή σε ενσωματωμένους τύπους ή αντικαταστήστε έναν υπάρχοντα ειναι ΑΠΑΓΟΡΕΥΜΕΝΟ.

Πότε πρέπει να υπερφορτώνετε τους χειριστές;

Θυμηθείτε το κύριο πράγμα: υπερφόρτωση τελεστών όταν και μόνο όταν έχει νόημα. Αν δηλαδή το νόημα της υπερφόρτωσης είναι προφανές και δεν φέρει κρυφές εκπλήξεις. Οι υπερφορτωμένοι χειριστές πρέπει να ενεργούν με τον ίδιο τρόπο όπως οι βασικές τους εκδόσεις. Φυσικά, οι εξαιρέσεις επιτρέπονται, αλλά μόνο σε εκείνες τις περιπτώσεις που συνοδεύονται από κατανοητές εξηγήσεις. Οι χειριστές<< и >> η τυπική βιβλιοθήκη iostream, η οποία σαφώς δεν συμπεριφέρεται όπως οι κανονικοί χειριστές.

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

Πίνακας a, b; Πίνακας c = a + b;

Ένα παράδειγμα κακής υπερφόρτωσης του τελεστή προσθήκης θα ήταν η προσθήκη αντικειμένων δύο παικτών σε ένα παιχνίδι. Τι εννοούσε ο δημιουργός της τάξης; Ποιο θα είναι το αποτέλεσμα; Δεν γνωρίζουμε τι κάνει η λειτουργία και επομένως είναι επικίνδυνο να χρησιμοποιήσετε αυτόν τον χειριστή.

Πώς να υπερφορτώνετε τους χειριστές;

Η υπερφόρτωση χειριστή είναι παρόμοια με την υπερφόρτωση συναρτήσεων με ειδικά ονόματα. Στην πραγματικότητα, όταν ο μεταγλωττιστής βλέπει μια έκφραση που περιέχει έναν τελεστή και έναν τύπο που ορίζεται από το χρήστη, αντικαθιστά αυτήν την έκφραση με μια κλήση στην κατάλληλη συνάρτηση του υπερφορτωμένου τελεστή. Τα περισσότερα από τα ονόματά τους ξεκινούν με τον τελεστή λέξης-κλειδιού και ακολουθεί ο προσδιορισμός του αντίστοιχου τελεστή. Όταν η ονομασία δεν αποτελείται από ειδικούς χαρακτήρες, για παράδειγμα, στην περίπτωση τελεστή cast ή διαχείρισης μνήμης (νέος, διαγραφή, κ.λπ.), ο χειριστής λέξης και ο προσδιορισμός του χειριστή πρέπει να διαχωρίζονται με ένα κενό (νέος τελεστής), Διαφορετικά ο χώρος μπορεί να αγνοηθεί (τελεστής + ).

Οι περισσότεροι τελεστές μπορούν να υπερφορτωθούν τόσο με μεθόδους κλάσης όσο και με απλές συναρτήσεις, αλλά υπάρχουν μερικές εξαιρέσεις. Όταν ο υπερφορτωμένος τελεστής είναι μια μέθοδος μιας κλάσης, ο τύπος του πρώτου τελεστή πρέπει να είναι αυτή η κλάση (πάντα * αυτή) και ο δεύτερος πρέπει να δηλωθεί στη λίστα παραμέτρων. Επίσης, οι δηλώσεις μεθόδων δεν είναι στατικές, εκτός από τις δηλώσεις διαχείρισης μνήμης.

Όταν ένας τελεστής υπερφορτώνεται σε μια μέθοδο κλάσης, αποκτά πρόσβαση στα ιδιωτικά πεδία της κλάσης, αλλά η κρυφή μετατροπή του πρώτου ορίσματος δεν είναι διαθέσιμη. Επομένως, οι δυαδικές συναρτήσεις συνήθως υπερφορτώνονται ως ελεύθερες συναρτήσεις. Παράδειγμα:

Κατηγορία Rational (δημόσιο: // Ο κατασκευαστής μπορεί να χρησιμοποιηθεί για σιωπηρή μετατροπή από int: Rational (αριθμητής int, παρονομαστής int = 1), Ορθολογικός τελεστής + (Rational const & rhs) const;); int main () (Rational a, b, c; int i; a = b + c; // ok, δεν απαιτείται μετατροπή a = b + i; // ok, σιωπηρή μετατροπή του δεύτερου ορίσματος a = i + c; // ERROR: το πρώτο όρισμα δεν μπορεί να μετατραπεί σιωπηρά)

Όταν οι unary τελεστές υπερφορτώνονται ως ελεύθερες συναρτήσεις, είναι διαθέσιμη σε αυτούς η σιωπηρή μετατροπή ορίσματος, αλλά συνήθως δεν χρησιμοποιείται. Από την άλλη πλευρά, οι δυαδικοί τελεστές χρειάζονται αυτήν την ιδιότητα. Επομένως, η κύρια συμβουλή θα ήταν η εξής:

Εφαρμόστε μοναδικούς τελεστές και δυαδικούς τελεστές όπως " Χ= "Ως μέθοδοι κλάσης και άλλοι δυαδικοί τελεστές ως ελεύθερες συναρτήσεις.

Ποιοι χειριστές μπορούν να υπερφορτωθούν;

Μπορούμε να υπερφορτώσουμε σχεδόν οποιονδήποτε τελεστή C ++ με τις ακόλουθες εξαιρέσεις και περιορισμούς:

  • Δεν μπορείτε να ορίσετε έναν νέο τελεστή όπως τελεστή **.
  • Οι ακόλουθοι τελεστές δεν μπορούν να υπερφορτωθούν:
    1. ?: (τριαδικός τελεστής);
    2. :: (πρόσβαση σε ένθετα ονόματα);
    3. ... (πρόσβαση σε πεδία).
    4. * (πρόσβαση στα πεδία κατά ευρετήριο).
    5. τελεστές sizeof, typeid και cast.
  • Οι ακόλουθοι τελεστές μπορούν να υπερφορτωθούν μόνο ως μέθοδοι:
    1. = (ανάθεση);
    2. -> (πρόσβαση σε πεδία κατά ευρετήριο)
    3. () (κλήση συνάρτησης).
    4. (πρόσβαση με ευρετήριο).
    5. -> * (πρόσβαση δείκτη σε πεδίο κατά δείκτη).
    6. τελεστές μετατροπής και διαχείρισης μνήμης.
  • Ο αριθμός των τελεστών, η σειρά εκτέλεσης και η συσχέτιση των τελεστών καθορίζονται από την τυπική έκδοση.
  • Τουλάχιστον ένας τελεστής πρέπει να είναι τύπου που ορίζεται από το χρήστη. Το Typedef δεν μετράει.

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

Ένας δυαδικός τελεστής, όπως, για παράδειγμα, ο τελεστής πρόσθεσης + πρέπει να οριστεί είτε ως μια μη στατική συνάρτηση που είναι μέλος μιας κλάσης με μία παράμετρο είτε ως μια συνάρτηση που δεν είναι μέλος μιας κλάσης με δύο Παράμετροι.

Τα μηνύματα του μεταγλωττιστή που σας δίνονται

Main.cpp: 17: 5: σφάλμα: Το C ++ απαιτεί έναν προσδιοριστή τύπου για όλες τις δηλώσεις τελεστής + (Cat a, Cat b) (^ main.cpp: 18: 16: σφάλμα: δεν είναι δυνατή η προετοιμασία του αντικειμένου επιστροφής τύπου "int" με rvalue τύπου "Cat *" επιστρέφει νέα Cat (a.value + b.value); ^ ~~~~~~~~~~~~~~~~~~~~~~~~

υποδεικνύετε ότι ο τελεστής που ορίσατε δεν έχει τύπο επιστροφής.

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

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

Ο χειριστής πρέπει να επιστρέψει το ίδιο το αντικείμενο, είτε με είτε χωρίς τον προσδιορισμό const.

Όπως αναφέρθηκε παραπάνω, μια δήλωση μπορεί να δηλωθεί ως μη στατική συνάρτηση μέλους μιας κλάσης με μία παράμετρο.

Σε αυτήν την περίπτωση, ο τελεστής + μπορεί να μοιάζει με αυτό

Κατηγορία Cat (ιδιωτική: τιμή int = 1; δημόσια: Cat (int _value) (τιμή = _τιμή;) Operator Cat + (const Cat & a) const (επιστροφή Cat (this-> value + a.value);));

Κατηγορία Cat (ιδιωτική: τιμή int = 1; δημόσια: Cat (int _value) (τιμή = _τιμή;) const τελεστής Cat + (const Cat & a) const (επιστροφή Cat (this-> value + a.value);)) ;

Θα μπορούσατε επίσης να υπερφορτώσετε τον τελεστή για αναφορές rvalue, όπως π.χ

χειριστής Cat + (const Cat && a) const (επιστροφή Cat (this-> value + a.value);)

χειριστής Cat + (Cat && a) const (επιστροφή Cat (this-> value + a.value);)

αλλά για μια τόσο απλή τάξη που δεν καταλαμβάνει μεγάλους πόρους, αυτό δεν έχει σημασία.

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

Λάβετε υπόψη σας, καθώς ο κατασκευαστής της κλάσης δηλώνεται ως κατασκευαστής μετατροπής, σε αυτήν την περίπτωση μπορείτε να προσθέσετε αντικείμενα της κατηγορίας Cat με αριθμούς. Για παράδειγμα,

#περιλαμβάνω κατηγορία Cat (ιδιωτική: τιμή int = 1; δημόσια: Cat (int _value) (τιμή = _τιμή;) const Operator Cat + (const Cat & a) const (επιστροφή Cat (this->

Το πόσο δικαιολογημένο είναι αυτό εξαρτάται από εσάς, με βάση την έννοια αυτού του τελεστή προσθήκης. Εάν δεν θέλετε να επιτρέψετε μια τέτοια σιωπηρή μετατροπή από έναν αριθμό σε ένα αντικείμενο της κλάσης Cat, τότε μπορείτε να δηλώσετε τον κατασκευαστή ως ρητή. Σε αυτήν την περίπτωση, το πρόγραμμα δεν θα μεταγλωττιστεί εάν προσπαθήσει να προσθέσει ένα αντικείμενο Cat σε έναν αριθμό.

#περιλαμβάνω κατηγορία Cat (ιδιωτική: τιμή int = 1; δημόσια: ρητή Cat (int _value) (τιμή = _τιμή;) const τελεστής Cat + (const Cat & a) const (επιστροφή Cat (this-> value + a.value);) ) ; int main () (Cat c1 (10); c1 + 5.5; return 0;)

Για αυτό το πρόγραμμα, ο μεταγλωττιστής θα εκδώσει ένα μήνυμα σφάλματος παρόμοιο με το παρακάτω

Prog.cpp: 24: 5: σφάλμα: δεν ταιριάζει με το "operator +" (οι τύποι τελεστών είναι "Cat" και "double") c1 + 5.5; ^

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

Μπορείτε να ορίσετε την ίδια τη συνάρτηση τόσο εντός του ορισμού κλάσης όσο και εκτός αυτής.

Για παράδειγμα,

#περιλαμβάνω κατηγορία Cat (ιδιωτική: τιμή int = 1; δημόσια: Cat (int _value) (τιμή = _τιμή;) φίλος const Cat operator + (const Cat & a, const Cat & b) (return Cat (a.value + b.value );)); int main () (Cat c1 (10); Cat c2 (5); Cat c3 = c1 + c2; επιστροφή 0;)

Λάβετε υπόψη ότι είναι κακή ιδέα να δηλώνετε μεταβλητές που ξεκινούν με υπογράμμιση. Γενικά, τέτοια ονόματα που ξεκινούν με υπογράμμιση προορίζονται για τον μεταγλωττιστή.

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

Cat (int value): τιμή (τιμή) ()

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

Συνεχίζοντας το θέμα:
Δίκτυα

Γεια σε όλους! Χρησιμοποιείτε Siri; Αν και αυτός είναι ένας υπέροχος φωνητικός βοηθός με τον οποίο μπορείτε πάντα να μιλήσετε, δεν το κάνω τόσο συχνά. Άλλωστε μέχρι τώρα...

Νέα άρθρα
/
Δημοφιλής