Προς το περιεχόμενο

[C++] - Δημιουργία Λαβυρίνθων


bnvdarklord

Προτεινόμενες αναρτήσεις

  • Moderators

Δεν ρώτησα αυτό. Η ερώτηση μου αφορούσε τα definitions και τα declarations μιας κλάσης στο ίδιο αρχείο, όχι όλο τον κωδικα.

 

Σου είπα, επειδή άμα τα πετάς όλα στο ίδιο αρχείο δυσκολεύει πολύ η κατανόηση του κώδικα. Δεν υπάρχει συντακτικό ή άλλο τεχνικό πρόβλημα, ο κώδικας θα γίνει compile είτε το έχεις σε ένα είτε σε 2 αρχεία, απλώς είναι πολύ πιο εύκολο για αυτόν που θα το διαβάσει. Επίσης, καλό είναι να μην χρησιμοποιείς using namespace σε header files διότι μπορεί να δημιουργήσεις ambiguity.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

  • Απαντ. 45
  • Δημ.
  • Τελ. απάντηση

Συχνή συμμετοχή στο θέμα

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

 

Αν τα έχω όλα σε ένα τότε τα definitions είναι μπροστά σου ενώ οι υλοποιήσεις ειναι ενα scroll down μακριά. Δεν αλλάζει κάτι άλλο. 

 

Είσαι βέβαιος οτι δεν ειναι και τεχνικό το θέμα;

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

  • Moderators

Εμένα προσωπικά δε με βολεύει καθόλου αυτό, και είναι ένας από τους βασικούς λόγους που απεχθάνομαι τη java. Προτιμώ με μια ματιά να έχω την κάτοψη της κλάσης για να ξέρω τι γίνεται. Όταν είπα "τεχνικό", εννοούσα ότι θα σου κάνει compile είτε το έχεις σε ένα είτε σε δύο αρχεία. Δεν είμαι ο πιο κατάλληλος για να σου πω τι γίνεται under the hood, αλλά μπορείς να δεις http://stackoverflow.com/questions/1305947/why-does-c-need-a-separate-header-file'>αυτό και αυτό (το 2ο είναι ένα wall of text το οποίο δεν έχω διαβάσει όλο, οπότε διάβασε όσο κρίνεις εσύ απαραίτητο :P )

  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Thanks. Θα τα δω αύριο μιας και είναι αργά τωρα.

 

Η κάτοψη της κλάσης που λες δεν λειπει πάντως πχ δες https://github.com/InsomniaProjects/maze-generator/blob/1a50bfd9e6ee517ccf6f3f07359cc212ee5c8a4b/src/Maze.h

 

Ας δούμε τι θα πουν και οι άλλοι σχετικά.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Λες αν έχω Color.h και Color.cpp και βαλω definition στο 1ο, έχω διπλό definition. Ωραία. Αν δεν κρατήσω όμως το Color.cpp και έχω μονο το Color.h αυτό το πρόβλημα δεν υπάρχει. Οπότε τι πειράζει να είναι σε ένα αρχείο;

 

Τα συγκεκριμενοποιείς τα πράγματα υπερβολικά και αυτό σε εμποδίζει να βγάλεις το σωστό συμπέρασμα.

 

Η ουσία του προβλήματος όταν εμφανίζεται είναι ότι αν κάνεις include το Color.h σε περισσότερα από ένα TU τότε αποκτάς πολλαπλά definitions για το Color::Yellow. Στο MazeGenerator αυτό μπορείς να το αποφύγες, αλλά αν έγραφες ένα πρόγραμμα σαν το Paint θα έκανες οπωσδήποτε include το Color.h σε 780 TUs οπότε no go.

 

Για μένα είναι conceptually το ίδιο σα να λες ότι διαβάζεις από ένα αρχείο ints σε ένα int[100] και δεν υπάρχει κανένα πρόβλημα γιατί στο αρχείο έχεις μέσα 50 ints. Το δέχομαι να πει κανείς ότι δε με πειράζει εδώ γιατί φύγε κακό απ' τα μάτια μου και μη σε ξαναδώ ποτέ, αλλά αν το δεις αφηρημένα σαν πρακτική το πρόβλημα υπάρχει.

 

 

Εμένα προσωπικά δε με βολεύει καθόλου αυτό, και είναι ένας από τους βασικούς λόγους που απεχθάνομαι τη java. Προτιμώ με μια ματιά να έχω την κάτοψη της κλάσης για να ξέρω τι γίνεται. Όταν είπα "τεχνικό", εννοούσα ότι θα σου κάνει compile είτε το έχεις σε ένα είτε σε δύο αρχεία.

 

Compile θα κάνει το κάθε ένα TU ξεχωριστά, αλλά αν τα έχεις στο ίδιο header το οποίο γίνεται include σε πολλαπλά TU και περιέχει non-inline functions ή static members όπως έχουμε εδώ, το πρόγραμμα σα σύνολο δε θα κάνει compile. Παραθέτω και λίγο από το standard μιας και δεν έγινε μέχρι τώρα.

 

1.3.26 [defns.well.formed]

well-formed program

C ++ program constructed according to the syntax rules, diagnosable semantic rules, and the One Definition Rule (3.2).

 

3.2/3

Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required. The definition can appear explicitly in the program, it can be found in the standard or a user-defined library, or (when appropriate) it is implicitly defined (see 12.1, 12.4 and 12.8). An inline function shall be defined in every translation unit in which it is odr-used.

 

 

(δεν παραθέτω άλλες παραγράφους άσχετες με το παράδειγμά μας)

 

Είναι φανερό ότι στην περίπτωση που συζητάμε υπάρχει ένα variable για το οποίο δίνονται πολλαπλά definitions, επομένως το πρόγραμμα δεν είναι well-formed επομένως δεν κάνει compile (το error βέβαια δίνεται σε επόμενο στάδιο από τον linker, αλλά αυτός είναι ο λόγος).

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Τα συγκεκριμενοποιείς τα πράγματα υπερβολικά και αυτό σε εμποδίζει να βγάλεις το σωστό συμπέρασμα.

 

Η ουσία του προβλήματος όταν εμφανίζεται είναι ότι αν κάνεις include το Color.h σε περισσότερα από ένα TU τότε αποκτάς πολλαπλά definitions για το Color::Yellow. Στο MazeGenerator αυτό μπορείς να το αποφύγες, αλλά αν έγραφες ένα πρόγραμμα σαν το Paint θα έκανες οπωσδήποτε include το Color.h σε 780 TUs οπότε no go.

 

 

Α μπράβο τώρα το κατάλαβα.

 

Έχοντας συνηθίσει πάντως σε C# και Java δεν μπορώ να σκεφτώ πρόγραμμα που όλα τα αρχεία δεν ειναι κλάσεις. Δηλαδη το 1 TU στην C++ προκύπτει από το αρχείο με την main. Τα άλλα TU απο τα χωριστά αρχεία που προκύπτουν απο τα definitions των κλάσεων. Άλλα TU πώς προκύπτουν;

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Ενα translation unit ειναι ενα source file. Οταν πας στο file->add item -> add source file, τοτε αυτο το αρχειο μαρκαρεται ως source. 

Πχ φτιαχνω ενα project και του βαζω ενα .cpp με ονομα Main.cpp και γραφω μονο αυτα

 

class Printer
{
public:
	void Print(const char* str);
};
int main()
{
	Printer printer;
	printer.Print("Hello World!");
	return 0;
}
Τιποτα αλλο, ουτε include ουτε τιποτα. Αυτο οπως το βλεπεις, κανει μια χαρα compile. Αυτο λοιπον ειναι ενα translation unit, μπορει να γινει compile (να βγαλει object αρχει δηλαδη) ενω ειναι "incomplete" ας πουμε. Βεβαια δεν μπορει να γινει build δηλαδη εκτελεσιμο.

 

Τωρα βαζω και αλλο ενα source αρχειο (δηλαδη αλλη translation unit), με ονομα PrinterClass.cpp και μεσα εχω αυτο

class Printer
{
public:
	void Print(const char* str);
};
#include <iostream>
void Printer::Print(const char* str)
{
	std::cout<<str<<std::endl;
}
Και αυτη κανει compile δηλαδη βγαζει object file, οπως επιβαλλεται να κανει μια translation unit.

Τωρα ομως το project μπορει να γινει και build, δηλαδη να γινει εκτελεσιμο, δηλαδη ο linker να παρει το Main.obj και το PrinterClass.obj να βγαλει το x.exe

 

Κατα 99.99% εδω εχεις χαθει. Δηλαδη στο πως γινεται δυο ξεχωριστα αρχεια τα οποια δεν εχουν καποιο link (πχ ενα include ) μεταξυ τους, να βγαζουν ενα προγραμμα που δουλευει. Πως βλεπει η main.cpp την υλοποιηση της PrinterClass.cpp.

 

 

 

Πως γινεται η ενωση; Με references! Αυτο ειναι ενα reference (aka declaration)

class Printer
{
public:
	void Print(const char* str);
};
Στο χ compiler το παραπανω θα βγαλει ενα συγκεκριμενο (ειναι κατι σα hash) ονομα για την συναρτηση Printer::Print (πχ εμενα μου βγαζει αυτο ?Print@Printer@@QAEXPBD@Z ). Σε οποιο translation unit ( *.cpp file) και να βαλω την παραπανω declaration, θα βγαλει το συγκεκριμενο ονομα. 

 

 

Το declaration einai ena reference στο definition. Μπορεις να εχει οσα θες declaration, αλλα μονο ενα definition. Εξαλλου, ενα reference δεν μπορει να δειχνει σε 2 πραματα :P .

 

 

Αυτα με τα TU και την ενωση τους.

Τωρα οι headers, οπως σου ειπα ειναι απλα copypaste. Ας πουμε οτι βαζω στο project ενα header test.h (Αυτο δεν θα μαρκαριστει απο το project ως source, ετσι δεν θα ειναι TU ). 

class OtherPrinter
{
public:
	void Print(const char* str);
};
#include <iostream>

void OtherPrinter::Print(const char* str)
{
	std::cout<<str<<std::endl;
}

Και αλλαξω την Main.cpp σε

#include "test.h"
class Printer
{
public:
	void Print(const char* str);
};
int main()
{
	Printer printer;
	printer.Print("Hello World!");
	OtherPrinter printer1;
	printer1.Print("Hello World1!");
	return 0;
}
Τοτε ουσιαστικα η main.cpp θα γινει

#include <iostream>
class OtherPrinter
{
public:
	void Print(const char* str);
};
void OtherPrinter::Print(const char* str)
{
	std::cout<<str<<std::endl;
}
class Printer
{
public:
	void Print(const char* str);
};
int main()
{
	Printer printer;
	printer.Print("Hello World!");
	OtherPrinter printer1;
	printer1.Print("Hello World1!");
	return 0;
}
(Οπου το include "test.h" θα μπει το περιεχομενο του test.h, αυτο κανει το include στην ουσια)

 

 

Τωρα σε αυτη την TU (main.cpp ) εχεις ενα reference το declaration της OtherPrinter, και ενα definition της OtherPrinter. Ολα καλα, μεχρι εδω. 

Αν ομως αλλαξεις και την Printer.cpp σε

class Printer
{
public:
	void Print(const char* str);
};
#include <iostream>
#include "test.h"

void Printer::Print(const char* str)
{
	OtherPrinter p;
	p.Print(str);
}

δηλαδη θα γινει 

class Printer
{
public:
	void Print(const char* str);
};
#include <iostream>
#include <iostream>
class OtherPrinter
{
public:
	void Print(const char* str);
};
void OtherPrinter::Print(const char* str)
{
	std::cout<<str<<std::endl;
}

void Printer::Print(const char* str)
{
	OtherPrinter p;
	p.Print(str);
}

Τοτε και στα δυο TU (object files) θα δημιουργηθουν δυο definition με το ιδιο ονομα. Κατι το οποιο δεν ειναι προβλημα για τον compiler, αλλα για τον linker... Σε ποια απο τις δυο definition θα βαλει το reference (το declaration του OtherPrinter) ? Στο Main.obj ή στο PrinterClass.obj
  • Like 3
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Κατα 99.99% εδω εχεις χαθει.

Καθόλου, γνωστά μου ειναι αυτά.

 

 

 

Τοτε και στα δυο TU (object files) θα δημιουργηθουν δυο definition με το ιδιο ονομα. Κατι το οποιο δεν ειναι προβλημα για τον compiler, αλλα για τον linker... Σε ποια απο τις δυο definition θα βαλει το reference (το declaration του OtherPrinter) ? Στο Main.obj ή στο PrinterClass.obj

Ναι ωραία. Εγώ σε αυτή την περίπτωση θα εβγαζα το ενα include ομως.

 

 

Εχω καταλάβει εντελώς το point. Χωρίζουμε τα αρχεία για να κάνουμε includes άφοβα.

 

Απλά όλες οι περιπτώσεις που σκέφτομαι μέχρι τώρα διορθώνονται απλά πειράζοντας τα includes. Υπάρχει όμως περίπτωση να μην γίνεται αυτο;

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Αφου το εχεις καταλαβει, γιατι επιμενεις με definition σε header? Κανε include το cpp αρχειο ως ειναι. Ή βαλε αλλο extension.. ξερω γω maze.bitmap.sub maze.bitmap.color.sub

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Έχοντας συνηθίσει πάντως σε C# και Java δεν μπορώ να σκεφτώ πρόγραμμα που όλα τα αρχεία δεν ειναι κλάσεις. Δηλαδη το 1 TU στην C++ προκύπτει από το αρχείο με την main. Τα άλλα TU απο τα χωριστά αρχεία που προκύπτουν απο τα definitions των κλάσεων. Άλλα TU πώς προκύπτουν;

 

Πάνω κάτω κάπως έτσι. Αλλά μπορεί να έχεις και free functions. Off the top μπορώ να σκεφτώ και μια άλλη περίπτωση (static configuration κάποιων πραγμάτων on application start με unnamed namespaces) αλλά είναι κάπως πιο ψαγμένο και σπάνιο σενάριο.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

  • Moderators

Επειδή δεν έχει header files ?

 

Επειδή είναι class μέσα σε class μέσα σε class και class μέσα σε declaration και classes παντού, ακόμα και για τα πιο απλά πράγματα.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Επειδή είναι class μέσα σε class μέσα σε class και class μέσα σε declaration και classes παντού, ακόμα και για τα πιο απλά πράγματα.

 

κατάλαβα λάθος επειδή είπες για κάτοψη της κλάσης

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

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

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

  • 11 μήνες μετά...

Έκανα compile την τελευταία έκδοση του κώδικα σε Linux.

Εκτέλεσα το πρόγραμμα και μου δημιούργησε το bitmap.

Το πρόβλημα είναι ότι δε μπορώ να το ανοίξω με κάποιο

ανάλογο πρόγραμμα καθώς εμφανίζεται ένα μήνυμα λάθους

που αφορά τους headers του bitmap:

 

Could not load image 'test.bmp'.

 

BMP image has unsupported header size

 

Το πρόγραμμα με το οποίο το δοκίμασα είναι το Image Viewer

του Ubuntu. Το δοκίμασα και με το Shotwell αλλά τίποτα.

 

EDIT: Έριξα μία ματιά στο Bitmap.h. Έχετε χρησιμοποιήσει

sizeof(HEADER) για τον υπολογισμό του header size. Το μέγεθος

του sizeof(HEADER) όμως είναι τουλάχιστον το άθροισμα των

μεγεθών των τύπων δεδομένων που περιλαμβάνουνται στο

struct (τουλάχιστον στη C).

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα

  • Δημιουργία νέου...