Προγραμματισμός Υπολογιστών με C++ ( 2012-13 ) 11η διάλεξη Ίων Ανδρουτσόπουλος http://www.aueb.gr/users/ion/ 1
Τι θα ακούσετε σήμερα Υπερφόρτωση του τελεστή εκχώρησης. Στατικές μεταβλητές, στατικές σταθερές και στατικές μέθοδοι τάξεων. Μη στατικές σταθερές τάξεων. 2
Προβλήματα με τον τελεστή = int main() { string arr1[] = {"Γιάννης", "Ιωάννης", arr2[] = {"Μαρία", "Μάρω"; MultiPerson p1(2, arr1), p2(2, arr2); MultiPerson other = p1; // Το other δεν υπήρχε. Χρησιμο- // ποιείται ο κατ/τής αντιγραφής. other = p2; // Το other υπάρχει ήδη. Χρησιμοποιείται ( other.operator=(p2 // ο τελεστής εκχώρησης. Ισοδύναμα: // Πρόβλημα: Διαρροή μνήμης και πιθανώς λάθος εκτέλεσης. Αν δεν ορίσουμε διαφορετικά, ο τελεστής εκχώρησης αντιγράφει μία-μία τις μεταβλητές. Σε τάξεις που χρησιμοποιούν δείκτες, συνήθως απαιτείται δικός μας κατ/τής, κατ/τής αντιγράφου, καταστροφέας και δική μας υπερφόρτωση του τελεστή εκχώρησης. 3
Πριν το other = p2: other howmanynames 2 names p2 howmanynames 2 names Τι συμβαίνει; Γιάννης Ιωάννης Μαρία Μάρω Μετά το other = p2: other howmanynames 2 names p2 howmanynames 2 names διαρροή μνήμης Γιάννης Ιωάννης Μαρία Μάρω 4
Προσπάθεια υπερφόρτωσης του = void MultiPerson::operator=(const MultiPerson& right) { delete []names; // To right μπορεί να έχει howmanynames = right.howmanynames; // άλλο αριθμό ονομάτων. names = new string[howmanynames]; for(unsigned i = 0; i < howmanynames; i++) { names[i] = right.names[i]; int main() { string arr1[] = {"Γιάννης", "Ιωάννης", arr2[] = {"Μαρία", "Μάρω"; MultiPerson p1(2, arr1), p2(2, arr2); MultiPerson other = p1; // Kατ/τής αντιγράφου. (Σε μερικούς (.= // μετ/τές, όμως, καλείται ο τελεστής other = p2; // Χρήση τελεστή εκχώρησης. ΟΚ. // other = p1 = p2; // Λάθος. Ο τελεστής επιστρέφει void. // other.operator=(p1.operator=(p2)); // Ισοδύναμη μορφή. 5
Βελτιωμένη υπερφόρτωση του = MultiPerson& MultiPerson::operator=(const MultiPerson& right) { delete []names; howmanynames = right.howmanynames; names = new string[howmanynames]; for(unsigned i = 0; i < howmanynames; i++) { names[i] = right.names[i]; return *this; int main() { string arr1[] = {"Γιάννης", "Ιωάννης", arr2[] = {"Μαρία", "Μάρω"; MultiPerson p1(2, arr1), p2(2, arr2); MultiPerson other = p1; // Χρήση κατ/τή αντιγραφής. other = p2; // Χρήση τελεστή εκχώρησης. ΟΚ. other = p1 = p2; // OK. Τώρα δουλεύει. // p1 = p1; // Πρόβλημα. Καταστρέφονται τα ονόματα! 6
Τελική υπερφόρτωση του = MultiPerson& MultiPerson::operator=(const MultiPerson& right) { if(this == &right) return *this; delete []names; howmanynames = right.howmanynames; names = new string[howmanynames]; for(unsigned i = 0; i < howmanynames; i++) { names[i] = right.names[i]; return *this; Η υπερφόρτωση του = ξεκινά πάντα με έλεγχο if(this == ). Η υπερφόρτωση του = μπορεί να γίνει μόνο μέσω μεθόδου. (Δεν επιτρέπεται να χρησιμοποιηθεί ο άλλος τρόπος υπερφόρτωσης, όπου υπερφορτώνεται συνάρτηση που δεν είναι μέθοδος τάξης. ) 7
Στατικές μεταβλητές τάξεων Ι αρχείο myclass.h: #ifndef _MYCLASS_H_ #define _MYCLASS_H_ #include <iostream> #include <string> using namespace std; class MyClass { string name; static unsigned counter; // Τη μοιράζονται όλα τα αντικείμενα. public: MyClass(string nn = "") { name = nn; counter++; MyClass(const MyClass& original); ~MyClass() { counter--; unsigned getcounter() const { return counter; static unsigned getcounter2() { return counter; ; #endif 8
Στατικές μεταβλητές τάξεων ΙΙ αρχείο myclass.cpp: #include "myclass.h" unsigned MyClass::counter = 0; MyClass::MyClass(const MyClass& original) { name = original.name; counter++; 9
Στατικές μεταβλητές τάξεων IΙΙ αρχείο main.cpp: #include "myclass.h" #include <iostream> using namespace std; int main() { MyClass m1("one"), m2("two"); cout << m1.getcounter(); // ΟΚ. Μη στατική μέθοδος. cout << m2.getcounter(); // Τυπώνεται το ίδιο. cout << m1.getcounter2(); // Στατική μέθοδος. Μπορεί να // κληθεί και μέσω αντικειμένου. cout << m2.getcounter2(); // Το ίδιο αποτέλεσμα. cout << MyClass::getCounter2(); // Δε χρειάζεται αντικείμενο. // cout << MyClass::getCounter(); // Λάθος. Μη στατική μέθοδος. Οι στατικές μέθοδοι έχουν πρόσβαση μόνο σε στατικά μέλη της τάξης (π.χ. η getcounter2() δεν έχει πρόσβαση στο name). 10
Στατικές σταθερές τάξεων class Test { static const unsigned n = 2; // Επιτόπου αρχικοποίηση static const // (επιτρέπεται μόνο με ακεραίους). int arr[n]; static const string s; public: void set(unsigned i, int value) { arr[i] = value; void print() const; ; const string Test::s = "prefix "; Μερικοί μεταγλωττιστές δεν υποστηρίζουν static const μέλη. Στην ανάγκη χρησιμοποιήστε #define. void Test::print() const { for(unsigned i = 0; i < n; i++) { cout << s << arr[i] << endl; int main() { Test t; t.set(0, 1); t.set(1, 2); t.print(); 11
Μη στατικές σταθερές τάξεων class NewTest { const unsigned n; // Κάθε αντικείμενο έχει τη δική του σταθερά n. int* arr; // Κάθε αντικείμενο έχει ένα πίνακα n θέσεων, με int dummy; // πιθανώς διαφορετικό n σε κάθε αντικείμενο. public: NewTest(unsigned nn = 1);... // Χρειάζομαι και καταστροφέα, κατ/τή αντιγραφής, τελεστή =. ; NewTest::NewTest(unsigned nn) : n(nn), dummy(1) { // n = nn; // Λάθος. Οι σταθερές δεν αρχικοποιούνται έτσι. // dummy = 1; // ΟΚ, αλλά καλύτερα αρχικοποίηση με «:». arr = new int[n]; // ΟΚ, αλλά καλύτερα αρχικοποίηση με «:». int main() { NewTest t(2); t.set(0, 1); t.set(1, 2); t.print(); 12
Μη στατικές σταθερές συνέχεια NewTest::NewTest(const NewTest& original) : { n(original.n), dummy(original.dummy), arr(new int[n]) for(unsigned i = 0; i < n; i++) { arr[i] = original.arr[i]; Αναδιατάσσονται σύμφωνα με τη σειρά τους στη δήλωση της τάξης. Γίνεται: n( ), arr( ), dummy( ). Η αρχικοποίηση με «:» επιτρέπεται μόνο στους κατασκευαστές. Η αναδιάταξη συμβαίνει σε όλους τους κατασκευαστές. NewTest::~NewTest() { delete []arr; 13
Μη στατικές σταθερές συνέχεια // Μόνο οι κατασκευαστές μπορούν να αρχικοποιήσουν με ":". NewTest& NewTest::operator=(const NewTest& right) { if(this = = &right) { return *this; // Δεν μπορούμε να αλλάξουμε το n μετά τη δημιουργία του // αντικειμένου: if(n!= right.n) { cerr << "Λάθος εκχώρησης" << endl; else { dummy = right.dummy; for(unsigned i = 0; i < n; i++) { arr[i] = right.arr[i]; return *this; Συνήθως τα μηνύματα λαθών τα στέλνουμε στο cerr. 14