alan2 Δημοσ. 28 Ιουνίου 2012 Δημοσ. 28 Ιουνίου 2012 το βιβλίο μου εχει το παράδειγμα ... int main() { ... double boxVolume(0.0); // Stores the volume of a box ... το boxVolume(0.0) δεν το αναφέρει πιο πρίν και δεν καταλαβαίνω τι ειδος μεταβλητής είναι που εχει και (0.0) εκτος αν αρχικοποιείται ετσι ο double.το πρόγραμμα παντως τρέχει. ολόκληρο το παράδειγμα >#include "stdafx.h" #include < iostream > using std::cout; using std::endl; class CBox // Class definition at global scope { public: double m_Length; // Length of a box in inches double m_Width; // Width of a box in inches double m_Height; // Height of a box in inches }; int main() { CBox box1; // Declare box1 of type CBox CBox box2; // Declare box2 of type CBox double boxVolume(0.0); // Stores the volume of a box box1.m_Height = 18.0; // Define the values box1.m_Length = 78.0; // of the members of box1.m_Width = 24.0; // the object box1 box2.m_Height = box1.m_Height - 10; // Define box2 box2.m_Length = box1.m_Length/2.0; // members in box2.m_Width = 0.25*box1.m_Length; // terms of box1 // Calculate volume of box1 boxVolume = box1.m_Height*box1.m_Length*box1.m_Width; cout << endl<< "Volume of box1 = " << boxVolume; cout << endl<< "box2 has sides which total "<< box2.m_Height+ box2.m_Length+ box2.m_Width<< " inches."; cout << endl // Display the size of a box in memory << "A CBox object occupies "<< sizeof box1 << " bytes."; cout << endl; return 0; }
moukoublen Δημοσ. 28 Ιουνίου 2012 Δημοσ. 28 Ιουνίου 2012 (και) Έτσι αρχικοποιείται ο double στην γλώσσα C++ της οποίας είναι ο κώδικας που παραθέτεις. π.χ. > #include <iostream> using namespace std; int main() { double d1 = 12.7; double d2(12.8); cout<<"d1 :"<<d1<<endl; cout<<"d2 :"<<d2<<endl; return 0; } έχει έξοδο > d1 :12.7 d2 :12.8 Ποιο βιβλίο είναι, αλήθεια;
alan2 Δημοσ. 28 Ιουνίου 2012 Μέλος Δημοσ. 28 Ιουνίου 2012 ευχαριστώ. Ivan Horton's Beggining Visual c++ 2010
defacer Δημοσ. 29 Ιουνίου 2012 Δημοσ. 29 Ιουνίου 2012 (επεξεργασμένο) Στην περίπτωση που η μεταβλητή που αρχικοποιείς είναι primitive (ενσωματωμένος αριθμητικός τύπος, pointer) τότε double d = 42 και double d(42) είναι ακριβώς το ίδιο πράγμα. Edit: αρχικά έγραψα πατάτα , έχω διορθώσει παρακάτω Στην περίπτωση που είναι class ή struct τότε υπάρχει τεχνικά διαφορά και γι' αυτό μπορεί μερικές φορές να παρατηρηθούν "περίεργες" συμπεριφορές (αλλά τυπικά όχι). Το original κείμενο (που παραθέτει ο moukoublen) το οποίο είναι λάθος έγραφε: υπάρχει σημαντική διαφορά γιατί η πρώτη μορφή καλεί πρώτα default constructor και μετά assignment operator, ενώ η δεύτερη μορφή καλεί μόνο τον copy constructor. Πολλές φορές βέβαια η class είναι γραμμένη με τέτοιο τρόπο και ο compiler κάνει αρκετές βελτιστοποιήσεις ούτως ώστε το τελικό παρατηρούμενο αποτέλεσμα είναι το ίδιο, αλλά αυτό δεν ισχύει πάντα. Επεξ/σία 29 Ιουνίου 2012 από defacer
moukoublen Δημοσ. 29 Ιουνίου 2012 Δημοσ. 29 Ιουνίου 2012 Στην περίπτωση που είναι class ή struct τότε υπάρχει σημαντική διαφορά γιατί η πρώτη μορφή καλεί πρώτα default constructor και μετά assignment operator, ενώ η δεύτερη μορφή καλεί μόνο τον copy constructor. Πολλές φορές βέβαια η class είναι γραμμένη με τέτοιο τρόπο και ο compiler κάνει αρκετές βελτιστοποιήσεις ούτως ώστε το τελικό παρατηρούμενο αποτέλεσμα είναι το ίδιο, αλλά αυτό δεν ισχύει πάντα. Πολύ ωραία. Πάμε λοιπόν τώρα σε μια απορία που έχω καιρό. Παραθέτω έναν προχειρογραμμένο κώδικα που ικανοποιεί απλά και μονο την απορία μου. > #include <iostream> using namespace std; class Foo{ private: int a; public: Foo(){ cout<<"Default Constructor"<<endl; this->a = 0; } Foo(const Foo& cp){ cout<<"Copy Constructor"<<endl; this->a = cp.a; } Foo& operator=(const Foo& rf){ cout<<"Assignment"<<endl; if (this == &rf) return *this; this->a = rf.a; return *this } }; Foo giveMeOne(){ Foo o; cout<<"Address in void: "<<&o<<endl; return o; } int main(int argc, char **argv) { Foo ena = giveMeOne(); cout<<"Address in main: "<<&ena<<endl; return 0; } Που έχει έξοδο > Default Constructor Address in void: 0x7fffab285860 Address in main: 0x7fffab285860 Δηλαδή όχι μόνο δεν τρέχει πρώτα default constructor και μετα Assignment (όπως και εγώ περίμενα οταν πρωτοείδα κατι τέτοιο) αλλά το αντικείμενο που δημιουργείται εσωτερικά της void είναι ακριβώς το ίδιο με αυτό που αποδίδεται στο ena. Όχι μόνο δεν αντιγράφεται δυαδικά ή δεν εκτελείται ο copy constructor, αλλά μιλάμε για ακριβώς το ίδιο αντικείμενο. Κάπου είχα πετύχει οτι το >Foo ena = giveMeOne(); Με την giveMeOne να επιστρέφει αυστηρά Foo (σκέτο). λέγεται initialisation (ή κάτι) και είναι τελείως διαφορετική διαδικασία απο το > Foo ena; ena = giveMeOne(); Κάτι που στην πράξη επιβεβαιώνεται απόλυτα. (Όντως στην δεύτερη περίπτωση πρώτα τρέχει ο default constructor για το αντικείμενο ena και έπειτα στο ena γίνεται assign το ο,τι επιστρέφει η giveMeOne. Στην οποία giveMeOne μάλιστα δε θα πρέπει να τρέξει ένας default αρχικά και κατά το return και ένας copy;;; ) Αλλά τοτε που είχα πετύχει αυτό το άρθρο -μέρος σε βιβλίο (δε θυμάμαι)- δεν έδωσα αρκετή σημασία. Και ρωτώ. Πρόκειται για optimazation του compiler ή για standard ζήτημα του προτύπου της γλώσσας;
moukoublen Δημοσ. 29 Ιουνίου 2012 Δημοσ. 29 Ιουνίου 2012 A ok. Είδα την διόρθωση. Άρα είναι όντως διαφορετικές αυτές οι δύο διαδικασίες που παρέθεσα. Επειδή μόνο χομπυστικα ασχολούμαι με c++ υπάρχουν στοιχεία της που δεν έχω κατανοήσει πλήρως ή που με μπερδεύουν. Σε σχέση τουλάχιστον με το ποιο απλό (reference) μοντέλο που ακολουθούν Java και C#.
defacer Δημοσ. 29 Ιουνίου 2012 Δημοσ. 29 Ιουνίου 2012 Καλά που έχεις την απορία αυτή για να θυμάμαι που και που πώς έχουν τα πράγματα μπας και σταματήσω να γράφω βλακείες... Έκανα λάθος πριν (πρέπει να είναι η 3η-4η φορά που θυμάμαι να κάνω αυτό το συγκεκριμένο λάθος ), οπότε έκανα edit και συνεχίζω με τη σωστή απάντηση που καλύπτει και την ερώτησή σου. Λοιπόν, η ουσία είναι ότι όταν κάνεις initialize class types αυτό μπορεί να γίνει με δύο μορφές: Direct-initialization (C++11, §8.5/15): The initialization that occurs in the forms >T x(a); T x{a}; as well as in new expressions (5.3.4), static_cast expressions (5.2.9), functional notation type conversions (5.2.3), and base and member initializers (12.6.2) is called direct-initialization. Copy-initialization (C++11, §8.5/14): The initialization that occurs in the form >T x = a; as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.5.1) is called copy-initialization. [Note: Copy-initialization may invoke a move (12.8). —end note ] Στη συνέχεια η §8.5/16 αναφέρει τι γίνεται στην περίπτωση όπου το T είναι class type (παραθέτω μόνο το σχετικό τμήμα): — If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. The applicable constructors are enumerated (13.3.1.3), and the best one is chosen through overload resolution (13.3). The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed. Με απλά λόγια, στην περίπτωση MyClass obj(args) αυτό που συμβαίνει είναι ότι ψάχνουμε και καλούμε τον καταλληλότερο constructor σύμφωνα με τους γνωστούς κανόνες για overload resolution. Τίποτα το περίεργο. Προσοχή ότι το ίδιο γίνεται και στην περίπτωση όπου το source type είναι το ίδιο με το destination type -- αυτό που λέει "or if it is copy-initialization where...", μόνο που εδώ προφανώς ο constructor που θα επιλεγεί θα είναι ο copy constructor. Δηλαδή το MyClass obj1, obj2 = obj1 θα έχει σαν αποτέλεσμα να κληθεί ο default constructor για το obj1 και ο copy constructor για το obj2. ΠΟΛΥ ΣΗΜΑΝΤΙΚΟ! Στην περίπτωσή σου όμως, εκ πρώτης όψεως θα έπρεπε να έχουμε μία κλήση του default constructor και μία του copy constructor σύμφωνα με τα όσα είπαμε. Γιατί δε συμβαίνει αυτό; Γιατί δημιουργείται ένα μόνο object; Η απάντηση είναι ότι στην προκειμένη, o compiler μπορεί να εφαρμόσει το λεγόμενο Named Return Value Optimization (NRVO). Αν δοκιμάσεις να απενεργοποιήσεις την πραγματοποίηση optimizations από τον compiler θα δεις όντως να δημιουργούνται παραπάνω αντικείμενα. — Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through overload resolution (13.3). If the conversion cannot be done or is ambiguous, the initialization is ill-formed. Εδώ περιγράφει περιπτώσεις του τύπου MyClass1 c1 = getMyClass2() όπου υπάρχει conversion από MyClass2 σε MyClass1 (η MyClass1 έχει constructor που δέχεται MyClass2, ή η MyClass2 έχει operator MyClass1). Συνεχίζει: The function selected is called with the initializer expression as its argument; if the function is a constructor, the call initializes a temporary of the cv-unqualified version of the destination type. The temporary is a prvalue. The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization. In certain cases, an implementation is permitted to eliminate the copying inherent in this direct-initialization by constructing the intermediate result directly into the object being initialized; see 12.2, 12.8. Στην περίπτωση αυτή λοιπόν καλούμε την κατάλληλη function που υλοποιεί τη μετατροπή για να κάνουμε initialize ένα temporary του destination type και στη συνέχεια με το temporary αυτό γίνεται direct-initialization της μεταβλητής. Για παράδειγμα, αν η MyClass1 έχει constructor που δέχεται MyClass2 τότε γράφοντας αυτό: >MyClass2 c2; MyClass1 c1 = c2; Ο compiler το μετατρέπει σε αυτό: >MyClass2 c2; MyClass1 c1(ΜyClass1(c2)); Σημαντική παρατήρηση αυτό που λέει στην τελευταία πρόταση: ακόμα και τότε, το sτandard ενθαρρύνει τον compiler να παραλείψει τη δημιουργία του temporary αν αυτό είναι δυνατόν (το οποίο όταν γίνει είναι και πάλι το γνωστό RVO).
moukoublen Δημοσ. 29 Ιουνίου 2012 Δημοσ. 29 Ιουνίου 2012 Πολύ ωραία, thanks για της διευκρινίσεις και τις παραθέσεις!
defacer Δημοσ. 29 Ιουνίου 2012 Δημοσ. 29 Ιουνίου 2012 A ok. Είδα την διόρθωση. Άρα είναι όντως διαφορετικές αυτές οι δύο διαδικασίες που παρέθεσα. Επειδή μόνο χομπυστικα ασχολούμαι με c++ υπάρχουν στοιχεία της που δεν έχω κατανοήσει πλήρως ή που με μπερδεύουν. Σε σχέση τουλάχιστον με το ποιο απλό (reference) μοντέλο που ακολουθούν Java και C#. Μόλις τελείωσα να διορθώνω την παραπάνω απάντηση, οπότε αν δεν την έχεις δει ακόμα (ή αν τη διάβασες στο ενδιάμεσο) ξαναδιάβασέ την αν θες. Πάντως για για τη C# υπάρχουν πάρα πολλά σημεία όπου είναι πραγματικά πολύ δύσκολο να καταλάβεις τι γράφει το standard γιατί οι αφηρημένες έννοιες και οι ορισμοί έρχονται τσουνάμι (έχω εμπειρία από πρώτο χέρι). Τα παραπάνω που παρέθεσα από τη C++ είναι σαν δυσκολία κατανόησης επιπέδου δημοτικού σε σχέση μ' αυτά τα σημεία της C# που φέρνω στο μυαλό μου τώρα.
moukoublen Δημοσ. 29 Ιουνίου 2012 Δημοσ. 29 Ιουνίου 2012 Λοιπόν η παραπομπή στη wikipedia για το NRVO ήταν αυτό ακριβώς που ήθελα. Θεωρητικά λοιπόν και κατά το return x; (στην περίπτωση που η υπογραφή της συνάρτησης είναι "καθαρό" object) γίνεται ένα copy αλλά και στο Object x = somethingThatReturnsObject(); γίνεται άλλο ένα copy. Απλά λόγω του optimization τελικά δεν δημιουργούνται ποτέ όλα αυτά τα temporary αντικείμενα. Όμορφα. Το θέμα είναι ότι στη σχολή μου βάζει ερωτήσεις με διάφορα initialisations και copy κτλ και σου ζητάει τι εκτελείται κάθε φορά. Είναι λοιπόν λίγο περίεργες αυτές η ερωτήσεις γιατί θεωρητικά υπάρχουν παραπάνω από μια σωστές απαντήσεις ανάλογα με το αν θα λάβεις υπόψη σου το stanrd μεν αλλά optimization δε, NRVO. (Έχω περάσει την C++ καιρό, απλά αναρωτιέμαι στο κατά πόσο λαμβάνονται υπόψη κατά την βαθμολόγηση όλα αυτά)
defacer Δημοσ. 29 Ιουνίου 2012 Δημοσ. 29 Ιουνίου 2012 Το θέμα είναι ότι στη σχολή μου βάζει ερωτήσεις με διάφορα initialisations και copy κτλ και σου ζητάει τι εκτελείται κάθε φορά. Είναι λοιπόν λίγο περίεργες αυτές η ερωτήσεις γιατί θεωρητικά υπάρχουν παραπάνω από μια σωστές απαντήσεις ανάλογα με το αν θα λάβεις υπόψη σου το stanrd μεν αλλά optimization δε, NRVO. (Έχω περάσει την C++ καιρό, απλά αναρωτιέμαι στο κατά πόσο λαμβάνονται υπόψη κατά την βαθμολόγηση όλα αυτά) Αυτά υποτίθεται ότι τα απαντάς με τη λογική ότι πουθενά δε γίνεται optimization αφού μιλάς ακαδημαϊκά και όχι για κάποιο συγκεκριμένο compiler. Οπότε ούτε NRVO κατά την άποψή μου. Για τη βαθμολόγηση παίζει να δίνεις πολύ μεγάλη αξία στις γνώσεις του καθηγητή. Ακαδημαϊκός καριέρας δεν παίζει με τίποτα να τα ξέρει αυτά τα πράγματα (φαντάζομαι υπάρχουν και εξαιρέσεις βέβαια). Αν θέλεις δοκίμασε να του κάνεις μια εύκολη ερώτηση του τύπου "έχω μπερδευτεί και δε μπορώ να καταλάβω πότε έχουμε direct-initialization και πότε copy-initialization" για να δεις τι θα σου απαντήσει. Ακόμα και χωρίς να έχεις την απαίτηση να θυμάται λεπτομέρειες μπορεί να απογοητευτείς.
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα