Προγραμματισμός Υπολογιστών με C++ ( 2012-13 ) 15η διάλεξη Ίων Ανδρουτσόπουλος http://www.aueb.gr/users/ion/ 1
Τι θα ακούσετε σήμερα Σχεδιότυπα συναρτήσεων και τάξεων. Αναπαράσταση δυαδικού δέντρου με δείκτες και σχεδιότυπα. 2
Σχεδιότυπα συναρτήσεων void myswap(int& a, int& b) { const int temp = a; a = b; b = temp; void myswap(string& a, string& b) { const string temp = a; a = b; b = temp; Εναλλακτικά: Αντί να ορίσουμε ξεχωριστές μορφές της συνάρτησης, καθορίζουμε πώς μπορεί να παραχθεί μια μορφή της για οποιονδήποτε τύπο Τ: template <typename T> void myswap(t& a, T& b) { const T temp = a; a = b; b = temp; 3
Χρήση σχεδιότυπων συναρτήσεων template <typename T> void myswap(t& a, T& b) { // Το σχεδιότυπο απαιτεί και τα δύο const T temp = a; // ορίσματα να είναι του ιδίου τύπου Τ. a = b; b = temp; int main() { int i = 10, j = 20; myswap(i, j); // Παράγεται αυτόματα η κατάλληλη συνάρτηση. cout << i << endl << j << endl; // Τυπώνει: «20, 10». string s1= "hi", s2 = "there"; myswap(s1, s2); // Παράγεται άλλη μορφή της συνάρτησης. cout << s1 << endl << s2 << endl; // Τυπώνει: «there», «hi». // myswap(i, s1); // Λάθος: ορίσματα διαφορετικών τύπων. // Δεν έχουμε σχεδιότυπο για αυτή την περίπτωση. 4
Άλλο παράδειγμα template <typename T1, typename T2> void printfirstpairs(const unsigned int n, const vector<t1>& v1, const vector<t2>& v2) { for(unsigned i = 0; i < v1.size() && i < v2.size() && i < n; i++) { cout << i << ": " << v1[i] << ", " << v2[i] << endl; int main() { vector<string> vec1; vector<unsigned int> vec2; vec1.push_back("one"); vec1.push_back("two"); vec1.push_back("three"); vec2.push_back(10); vec2.push_back(20); vec2.push_back(30); printfirstpairs(2, vec1, vec2); // Τυπώνει: 0: one, 10 1: two, 20 5
Σχεδιότυπα τάξεων template <typename T1, typename T2> class Pair { T1 first; T2 second; public: Pair(const T1& firstin, const T2& secondin) : first(firstin), second(secondin) { T1& getfirst() { return first; T2& getsecond() { return second; ; int main() { Pair<string, unsigned> p1("word1", 100), p2("word2", 200); vector< Pair<string, unsigned> > vec; vec.push_back(p1); vec.push_back(p2); vec[1].getfirst() = "word3"; cout << vec[0].getfirst() << ", " << vec[0].getsecond() << endl << vec[1].getfirst() << ", " << vec[1].getsecond() << endl; 6
Δυαδικό δέντρο 10 20 30 40 50 60 70 7
Παράσταση δυαδικού δέντρου n1 10 left right n2 20 left right n3 30 left right n4 n5 n6 n7 40 left right 50 left right 60 left right 70 left right 0 0 0 0 0 0 0 0 8
Η τάξη Node class Node { const int i; const Node* const left; const Node* const right; public: ; Node(const int iin, const Node* const leftin = 0, const Node* const rightin = 0) : i(iin), left(leftin), right(rightin) { void printtree() const; Στη C++11 υπάρχει η ειδική τιμή nullptr για δείκτες που δε δείχνουν πουθενά. Π.χ: leftin = nullptr; 9
Παράδειγμα χρήσης της Node int main() { Node n4(40), n5(50), n6(60), n7(70), n2(20, &n4, &n5), n3(30, &n6, &n7), n1(10, &n2, &n3); n1.printtree(); 10
Αναδρομική εκτύπωση void Node::printTree() const { cout << i << endl; if(left!= 0) { left->printtree(); if(right!= 0) { right->printtree(); // Τυπώνει με αναζήτηση πρώτα σε βάθος (DFS). // Στο δέντρο του παραδείγματος τυπώνει: // 10, 20, 40, 50, 30, 60, 70. 11
Τροποποιήσεις για εκτύπωση με ( BFS ) αναζήτηση πρώτα σε πλάτος class Node { const int i; const Node* const left; const Node* const right; Έτοιμη υλοποίηση ουράς με: #include <queue> static void printmanytrees(queue<node>& printqueue); public: Node(const int iin, const Node* const leftin = 0, const Node* const rightin = 0) : i(iin), left(leftin), right(rightin) { void printtree() const; ; 12
Εκτύπωση με BFS void Node::printTree() const { queue<node> printqueue; printqueue.push(*this); printmanytrees(printqueue); // Στην αρχική κλήση έχουμε ένα μόνο κόμβο στην // ουρά: τη ρίζα του δέντρου που θέλουμε να τυπώσουμε. // Τυπώνει με αναζήτηση πρώτα σε πλάτος (BFS). // Στο δέντρο του παραδείγματος τυπώνει: // 10, 20, 30, 40, 50, 60, 70. 13
Εκτύπωση με BFS συνέχεια void Node::printManyTrees(queue<Node>& printqueue) { while(!(printqueue.empty( ))) { Node& currentnode = printqueue.front(); cout << currentnode.i << endl; if(currentnode.left!= 0) { printqueue.push(*(currentnode.left)); if(currentnode.right!= 0) { printqueue.push(*(currentnode.right)); printqueue.pop(); 14
Γενίκευση της Node template <typename Type> class Node { const Type i; const Node* const left; const Node* const right; public: Node(const Type iin, const Node* const leftin = 0, const Node* const rightin = 0) : i(iin), left(leftin), right(rightin) { void printtree() const; ; 15
Μέθοδος σχεδιότυπου τάξης template <typename Type> void Node<Type>::printTree() const { cout << i << endl; if(left!= 0) { left->printtree(); if(right!= 0) { right->printtree(); Αυτές είναι οι αλλαγές που κάνουμε στον κώδικα που τυπώνει με DFS. Αντίστοιχες αλλαγές στην printtree και printmanytrees του κώδικα που τυπώνει με BFS. 16
Παράδειγμα χρήσης σχεδιότυπου int main() { Node<int> n4(40), n5(50), n6(60), n7(70), n2(20, &n4, &n5), n3(30, &n6, &n7), n1(10, &n2, &n3); n1.printtree(); Node<string> m4("forty"), m5("fifty"), m6("sixty"), m7("seventy"), m2("twenty", &m4, &m5), m3("thirty", &m6, &m7), m1("ten", &m2, &m3); m1.printtree(); 17
Σχεδιότυπα και αρχεία κεφαλίδας Όσα τμήματα του κώδικα ξεκινούν με template<...> μπαίνουν στο αρχείο κεφαλίδας. Δηλαδή στην περίπτωση των σχεδιότυπων τάξεων, το αρχείο κεφαλίδας περιέχει και τους ορισμούς των μεθόδων (τα κυρίως μέρη). Υπάρχει μια λέξη-κλειδί export που επιτρέπει την τοποθέτηση μεθόδων σχεδιότυπων τάξεων σε αρχεία.cpp, αλλά πολλοί μεταγλωττιστές δεν την υποστηρίζουν ακόμα. 18