Διαδρομές
/
C++
C++
/
Ασκήσεις
/
Η δύναμη της Τροίας
Η δύναμη της Τροίας

Η δύναμη της Τροίας

Άσκηση εκμάθησης

Εισαγωγή

Η λέξη-κλειδί auto στη C++

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

Παράδειγμα χρήσης

Δες το παρακάτω παράδειγμα, όπου το auto χρησιμοποιείται για να δηλώσεις μεταβλητές:

auto dragon_population{3};          // dragon_population is deduced as an integer
auto westeros{7.7777};              // westeros is deduced as a double
auto wedding_location{"The Twins"}; // wedding_location is deduced as a const char*, not std::string

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

Συμπερασματολογία τύπων

Η λέξη-κλειδί auto σε βοηθά να γράφεις πιο σύντομο και πιο ευανάγνωστο κώδικα, μειώνοντας τη φλυαρία των ρητών τύπων.

const std::vector<std::string> pigeon_pie{"flour", "butter", "pigeon", "salt"};
auto purple_wedding_pie{pigeon_pie};
purple_wedding_pie.emplace_back("the strangler");

Σε αυτόν τον βρόχο, το auto συμπεραίνει τον τύπο του purple_wedding_pie ως std::vector<std::string>, αποφεύγοντας την ανάγκη να προσδιορίσεις ξανά ρητά τον τύπο.

Συμβατότητα

Η λέξη-κλειδί auto είναι συμβατή με διάφορες δομές της C++, γεγονός που την κάνει ένα ευέλικτο εργαλείο στον σύγχρονο προγραμματισμό σε C++.

auto& element{array[0]};           // reference to an element
const auto object{otherObject};    // const type version of otherObject's type
auto* ptr{&x};                     // pointer to x with the same type as x, but as a pointer.

Σε επόμενες έννοιες θα βλέπουμε συχνά τη λέξη-κλειδί auto με εκφράσεις λάμδα, βρόχους for βασισμένους σε εύρος και επαναλήπτες.

Έξυπνοι δείκτες

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

Γενική σύνταξη

Οι έξυπνοι δείκτες συνήθως υλοποιούνται ως πρότυπα κλάσεων στην πρότυπη βιβλιοθήκη της C++. Οι δύο πιο συχνά χρησιμοποιούμενοι έξυπνοι δείκτες είναι οι std::unique_ptr και std::shared_ptr.

Μοναδικοί δείκτες

Ο std::unique_ptr είναι ένας έξυπνος δείκτης που κατέχει το αντικείμενο αποκλειστικά. Διασφαλίζει ότι ανά πάσα στιγμή μόνο ένα αντικείμενο std::unique_ptr κατέχει τον πόρο. Όταν ο std::unique_ptr που κατέχει τον πόρο καταστραφεί ή επαναρυθμιστεί, καταστρέφει αυτόματα το αντικείμενο και απελευθερώνει τη μνήμη του.

#include <memory>
// Declaring and defining a unique pointer
auto rightful_king_of_england = std::make_unique<std::string>("Excalibur");

// Unique pointers cannot be copied or assigned
auto mordred = rightful_king_of_england; // Error: Cannot copy a unique_ptr

Πλεονεκτήματα του std::make_unique()

Όταν δημιουργείς έναν std::unique_ptr, είναι προτιμότερο να χρησιμοποιείς τον std::make_unique() αντί να χρησιμοποιείς απευθείας το new για να δεσμεύσεις μνήμη. Ο std::make_unique() προσφέρει αρκετά πλεονεκτήματα:

  1. Ασφάλεια έναντι εξαιρέσεων: Ο std::make_unique() εγγυάται ασφάλεια έναντι εξαιρέσεων. Αν πεταχτεί μια εξαίρεση κατά την κατασκευή του αντικειμένου, η μνήμη θα αποδεσμευτεί αυτόματα, αποτρέποντας διαρροές μνήμης.
  2. Σαφήνεια: Η χρήση του std::make_unique() κάνει τον κώδικα πιο σαφή και πιο σύντομο. Εξαλείφει την ανάγκη να προσδιορίσεις ρητά τον τύπο που δεσμεύεται, αφού τα ορίσματα του προτύπου συμπεραίνονται αυτόματα.
  3. Ευκαιρίες βελτιστοποίησης: Οι μεταγλωττιστές έχουν τη δυνατότητα να βελτιστοποιήσουν τον std::make_unique() πιο αποτελεσματικά από ό,τι τη χειροκίνητη δέσμευση μνήμης με το new, κάτι που μπορεί να οδηγήσει σε καλύτερες επιδόσεις.
  4. Αποφυγή κακής χρήσης: Η διαγραφή του υποκείμενου πόρου είναι δυνατή όταν ο std::unique_ptr κατασκευάζεται χειροκίνητα. Αυτό θα οδηγούσε σε απροσδιόριστη συμπεριφορά, όταν ο std::unique_ptr προσπαθεί να τον διαγράψει στο τέλος της εμβέλειάς του.

Κοινοί δείκτες

Ο std::shared_ptr είναι ένας έξυπνος δείκτης που επιτρέπει σε πολλά αντικείμενα std::shared_ptr να μοιράζονται την κυριότητα του ίδιου πόρου. Παρακολουθεί πόσοι κοινοί δείκτες αναφέρονται στον πόρο και αποδεσμεύει τη μνήμη μόνο όταν ο τελευταίος κοινός δείκτης που κατέχει τον πόρο βγει από την εμβέλεια ή επαναρυθμιστεί.

// Declaring and defining a shared pointer to a dynamically allocated string
auto martian_congressional_republic = std::make_shared<std::string>("protomolecule");

// Creating more shared pointer that shares ownership
auto outer_planets_alliance = martian_congressional_republic;
auto united_nations = martian_congressional_republic;
Caution

Στη C++17 και σε παλαιότερες εκδόσεις, η χρήση του std::shared_ptr με πίνακες μέσω του std::make_shared<T[]> δεν υποστηρίζεται άμεσα. Αν και είναι δυνατή η δέσμευση πινάκων με το std::make_shared<T[]>, η δημιουργία κοινών δεικτών απευθείας από αυτούς μπορεί να οδηγήσει σε απροσδιόριστη συμπεριφορά, λόγω διαφορών στη διαχείριση μνήμης ανάμεσα σε μεμονωμένα αντικείμενα και πίνακες. Αντ' αυτού, σκέψου να χρησιμοποιήσεις το std::vector ή προσαρμοσμένες συναρτήσεις διαγραφής για να διαχειρίζεσαι αποτελεσματικά πίνακες με κοινούς δείκτες. Να διασφαλίζεις πάντα τη συμβατότητα με τον μεταγλωττιστή σου και την υλοποίηση της πρότυπης βιβλιοθήκης σου, όταν ασχολείσαι με δεσμεύσεις πινάκων και κοινούς δείκτες στη C++17.

Πλεονεκτήματα του std::make_shared()

Όπως ο std::make_unique(), έτσι και ο std::make_shared() προσφέρει οφέλη όπως καλύτερη αποδοτικότητα μνήμης, ασφάλεια έναντι εξαιρέσεων και αναγνωσιμότητα. Συνδυάζει τη δέσμευση μνήμης για το μπλοκ ελέγχου και το διαχειριζόμενο αντικείμενο σε μία μόνο λειτουργία, βελτιώνοντας την αποδοτικότητα και μειώνοντας τον κίνδυνο διαρροών μνήμης. Επιπλέον, η αυτόματη συμπερασματολογία των ορισμάτων του προτύπου απλοποιεί τον κώδικα και βελτιώνει την αναγνωσιμότητα. Η χρήση του std::make_shared() προάγει καθαρότερο, ασφαλέστερο και πιο αποδοτικό κώδικα όταν δουλεύεις με αντικείμενα std::shared_ptr στη C++.

Συμβουλές χρήσης

Χρησιμοποίησε έξυπνους δείκτες κατά προεπιλογή: std::unique_ptr για αποκλειστική κυριότητα και std::shared_ptr για κοινή κυριότητα. Κράτα τους ακατέργαστους δείκτες για αναφορές χωρίς κυριότητα ή όταν συνεργάζεσαι με παλαιό κώδικα. Στις περισσότερες περιπτώσεις, ο std::unique_ptr αρκεί για αποκλειστική κυριότητα, καθώς προσφέρει ελαφριά διαχείριση μνήμης χωρίς την επιβάρυνση της μέτρησης αναφορών. Ο std::shared_ptr πρέπει να χρησιμοποιείται με φειδώ, καθώς εισάγει επιβάρυνση και πολυπλοκότητα, εκτός αν χρειάζεται πραγματικά κοινή κυριότητα.

Οδηγίες

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

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

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

1. Φέρε τους ανθρώπους στον κόσμο της Τροίας

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

Σου παρέχονται βασικές υλοποιήσεις των struct artifact και power. Υλοποίησε ένα struct (ή κλάση) human που έχει έναν έξυπνο δείκτη προς μια μεταβλητή-μέλος artifact με το όνομα possession. Κάθε τεχνούργημα μπορεί να κατέχεται μόνο από έναν άνθρωπο κάθε δεδομένη στιγμή.

Ένα human πρέπει να έχει δύο επιπλέον μεταβλητές-μέλη. Η μία κρατά το own_power του και η άλλη είναι μια δύναμη από την οποία επηρεάζεται, το influenced_by. Τόσο το own_power όσο και το influenced_by είναι έξυπνοι δείκτες προς powers. Κάθε power μπορεί να ανήκει σε έναν μόνο άνθρωπο, αλλά ταυτόχρονα να επηρεάζει και άλλους ανθρώπους.

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

human mindy_mccready{};
mindy_mccready.possession;
// => nullptr
mindy_mccready.own_power;
// => nullptr
mindy_mccready.influenced_by;
// => nullptr

2. Φέρε τα τεχνουργήματα στην Τροία

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

Όρισε τη συνάρτηση give_new_artifact που δεν επιστρέφει τίποτα αλλά παίρνει μια αναφορά σε ένα human και μια string. Με τη string θα πρέπει να ορίσει ένα νέο αντικείμενο artifact και να ορίσει ανάλογα τον δείκτη possession του human. Η συνάρτηση δεν θα πρέπει να επιστρέφει τίποτα.

human erik_magnus_lehnsherr{};
give_new_artifact(erik_magnus_lehnsherr, "Mind shielding helmet");

erik_magnus_lehnsherr.possession->name;
// "Mind shielding helmet"

3. Κάνε τα αντικείμενα ανταλλάξιμα

Ο κόσμος της Τροίας είναι όλος αλληλεπίδραση. Θέλεις οι άνθρωποι να κάνουν ανταλλαγές ανταλλάσσοντας τα αποκτήματά τους.

Γράψε μια συνάρτηση exchange_artifacts που δεν επιστρέφει τίποτα αλλά παίρνει δύο έξυπνους δείκτες τεχνουργημάτων για να ανταλλάξει τα αντικείμενα. Θυμήσου ότι δεν μπορείς να αντιγράψεις ένα unique_ptr. Αυτό περιλαμβάνει τη χρήση σε παραμέτρους συνάρτησης. Χρησιμοποίησε αντ' αυτού μια αναφορά στο unique_ptr.

human uchiha{};
give_new_artifact(uchiha, "konoha headband");
human uzumaki{};
give_new_artifact(uzumaki, "forehead protector");

exchange_artifacts(uchiha.possession, uzumaki.possession);

uchiha.possession->name;
// "forehead protector"
uzumaki.possession->name;
// "konoha headband"

4. Δώσε δύναμη στους ανθρώπους

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

Όρισε τη συνάρτηση manifest_power που δεν επιστρέφει τίποτα αλλά παίρνει μια αναφορά σε ένα human και μια string. Με τη string θα πρέπει να ορίσει ένα νέο αντικείμενο power και να ορίσει ανάλογα τον δείκτη own_power του human. Η συνάρτηση δεν θα πρέπει να επιστρέφει τίποτα.

human eleven {};
manifest_power(eleven, "psychokinesis");

eleven.own_power->effect;
// "psychokinesis"

5. Χρησιμοποίησε τη δύναμη

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

Γράψε μια συνάρτηση void use_power που παίρνει δύο αναφορές σε ανθρώπους. Ο πρώτος άνθρωπος είναι αυτός που ασκεί τη δύναμη και ο δεύτερος αντιπροσωπεύει τον στόχο. Ο δείκτης influenced_by του στόχου θα πρέπει να δείχνει στη δύναμη αυτού που την ασκεί.

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

human pamela_isley{};
manifest_power(pamela_isley, "control pheromones");

human count_vertigo{};
use_power(pamela_isley, count_vertigo);
count_vertigo.influenced_by->effect;
// "control pheromones"

6. Παρακολούθησε την ένταση της δύναμης

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

Όρισε τη συνάρτηση power_intensity, που παίρνει έναν άνθρωπο και επιστρέφει την ένταση της δύναμής του ως int. Αν το άτομο δεν έχει δύναμη, η τιμή επιστροφής θα πρέπει να είναι 0. Διαφορετικά, η ένταση θα πρέπει να αντανακλά αυτόν που ασκεί τη δύναμη και όλους τους ανθρώπους που επηρεάζονται αυτή τη στιγμή.

human jean_grey{};
manifest_power(jean_grey, "uplifting personality");

human scott{};
human logan{};
human ororo{};

use_power(jean_grey, ororo);
use_power(jean_grey, logan);
use_power(jean_grey, scott);

power_intensity(jean_grey);
// 4
Επεξεργασία μέσω GitHub Ο σύνδεσμος ανοίγει σε νέο παράθυρο ή καρτέλα
C++ Exercism

Έτοιμος να ξεκινήσεις την άσκηση Η δύναμη της Τροίας;

Γράψου στο Exercism για να μάθεις και να κατακτήσεις C++ με 19 έννοιες100 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.