Can you correct this design

Started by
25 comments, last by Zahlman 15 years, 8 months ago
Am I the only one who doesn't create a window class?

I put my window code in my main.cpp file.

Is the idea of creating a windows class so it can be used in other apps or just oop neatness?
Advertisement
On the topic of construction and initialization:

Can you all clarify what you mean by "construction"? Are you talking about the "building" of the object, or of the allocation of memory, calling of constructor, and that kind of stuff.

For example...

We're making a random maze generator. I have a generic Maze class. It has a constructor which initializes it to a maze of the given dimensions where all possible places are walls. I then have a static member function of Maze CreateRandomPerfectMaze(), which creates a new Maze, and then sets its walls so that it is a random, perfect maze.

In this example, what is the construction of the Maze? Is it the c'tor of Maze or the CreateRandomPerfectMaze()? Do you consider the initialization and construction of the maze to be separate or together?

Another more generic example: I've got a class and I want to load its state from an XML file. I call a default c'tor which puts it in a well-defined, initialized, but largely useless state, then I read in the XML file and set the class's properties appropriately. Same questions as above.

Thanks.

@Anda: I used to just put the window code in main(), but I've since switched to a Window class. Partly I find it more consistent (eg: physics engines have a Simulation() class, or w/e)., but mostly I just find that it clears up the main() function, which tends to be overly large and messy.
Quote:Original post by Ezbez
I've got a class and I want to load its state from an XML file. I call a default c'tor which puts it in a well-defined, initialized, but largely useless state, then I read in the XML file and set the class's properties appropriately. Same questions as above.


So your code looks like this:

Obj obj;obj.load(fileName);


What is the benefit of first creating an "empty" object and only initializing it later? In other words, why not just do this:

Obj obj(fileName);


The disadvantage of the former is that you can forget to call the load() function, and then you'll be working with an uninitialized object. This is basically why constructors exist.

The disadvantage of the latter is that if you need to delay the creation of the object or you need to be able to recreate the object (that is, destroy it and create it with a different state), you'll need to use dynamic memory allocation, which I suppose can be an issue sometimes.

(although for the re-creation of the object, you might be able to use a memory pool.)
Sorry to Paic and others, this thread seems to have slipped under my radar since my first post; as for 'why' separating construction from initialisation is bad I think Brother Bob has it covered beautifully.

I'll tackle some other things though.

Quote:Original post by anda
I put my window code in my main.cpp file.
If not using a class I would probably stick it in a separate function in it's own file and make a call to it from main.

Quote:Is the idea of creating a windows class so it can be used in other apps or just oop neatness?
Both.
You wouldn't use a class for the sake of using a class though; if it can be done sensibly within a simple function with no state, as I think GLUT can be, then it makes sense to do so. A class is just a collection of functions and state all combined into one unit of code.

I don't think having a GLUTWindow class makes complete sense however, as far as I know you cannot have more than one GLUT window and you don't need to hold onto any variables when using it. I stand to be corrected here though.

I don't use GLUT for windowing for the same reasons that Skeezix is having problems: In order to do anything remotely non-trivial you have to resort to global or static variables - take the glut key handler function as an example, the only way for it to modify the state of the rest of the program in response to a keypress would be to access some global, or static, variable. The GLUT windowing API just isn't sophisticated, or modern, enough to allow for any of the nicer OO options. In comparison the Win32 library, despite being both old and a C (not C++) library, does have the sophisticatation to avoid globals and statics which allows you to wrap things up in a more OO way.

Quote:Original post by Ezbez
Can you all clarify what you mean by "construction"? Are you talking about the "building" of the object, or of the allocation of memory, calling of constructor, and that kind of stuff.
By construction I/we mean object creation, also termed instantiation (creating an instance of a type).
When you create an object, it gets memory allocated for it, name bindings are created and initialisation code is invoked. In C++ this initialisation code is called the constructor and you're free to optionally provide your own constructors if the trivial defaultly provided ones don't suffice.

So even as a language feature, initialisation is part of the construction process. By providing a separate init function you're essentially cheating the language and also allowing your class instance to exist in some invalid or useless state.

Quote:We're making a random maze generator. I have a generic Maze class. It has a constructor which initializes it to a maze of the given dimensions where all possible places are walls.
Personally I would imagine such a constructed maze to be an 'empty' maze, one without any walls that is ready to be populated with them, rather than an already 'full' maze.

Quote:I then have a static member function of Maze CreateRandomPerfectMaze(), which creates a new Maze, and then sets its walls so that it is a random, perfect maze.
The premise sounds good, but I don't see the use of making it a static member function unless it's accessing non-public static variables? Otherwise do yourself a favour by making it a non-member function to increase encapsulation.

Quote:In this example, what is the construction of the Maze? Is it the c'tor of Maze or the CreateRandomPerfectMaze()? Do you consider the initialization and construction of the maze to be separate or together?
The constructor itself initialises a maze to a valid, useable and useful state, even if that state is considered empty (or full in your case).

Your function constructs such a maze, then it populates it with new walls (termed mutation or frobnication) which simply changes it from an already valid state to some other valid state.

What then happens is that this populated maze is returned and used for the copy construction of a second maze object; the copy constructor will initialise this second object to the valid, useable and useful state of the returned maze.

In short: There are two maze objects being constructed, their constuctors both handle initialisation to a valid state and the CreateRandomPerfectMaze function also does some mutation on that state.

So you haven't separated construction from initialisation; furthermore this is such a common pattern that the compiler can perform certain return value optimisations which would be harder or impossible had you handled initialisation separately.

Quote:Another more generic example: I've got a class and I want to load its state from an XML file. I call a default c'tor which puts it in a well-defined, initialized, but largely useless state, then I read in the XML file and set the class's properties appropriately. Same questions as above.
It depends what you mean by 'useless'; if the state legitimately has no use then there is no reason for you to ever invoke the constructor that initialises to that state in the first place - it's just like any other function, if you had a function that produced a meaningless result then why would you ever wish to call it? Why would you ever even write such a function?

The natural thing to do would be to load the state from the XML file and then create the object. Or have the object's constructor load it's own state from the file using some kind of abstraction (you probably wouldn't want the object to deal directly with the file).

[Edited by - dmatter on August 16, 2008 10:36:25 AM]
Thanks for the clarifications, dmatter.

Quote:Original post by dmatter
Quote:We're making a random maze generator. I have a generic Maze class. It has a constructor which initializes it to a maze of the given dimensions where all possible places are walls.

Personally I would imagine such a constructed maze to be an 'empty' maze, one without any walls that is ready to be populated with them, rather than an already 'full' maze.


Sure, I just said already full, since the method I have worked with in the past to generate random, perfect mazes worked by removing walls rather than adding them.

Quote:
Quote:I then have a static member function of Maze CreateRandomPerfectMaze(), which creates a new Maze, and then sets its walls so that it is a random, perfect maze.

The premise sounds good, but I don't see the use of making it a static member function unless it's accessing non-public static variables? Otherwise do yourself a favour by making it a non-member function to increase encapsulation.


Good suggestion, though this was only example.

Quote:
Quote:In this example, what is the construction of the Maze? Is it the c'tor of Maze or the CreateRandomPerfectMaze()? Do you consider the initialization and construction of the maze to be separate or together?

The constructor itself initialises a maze to a valid, useable and useful state, even if that state is considered empty (or full in your case).

Your function constructs such a maze, then it populates it with new walls (termed mutation or frobnication) which simply changes it from an already valid state to some other valid state.

What then happens is that this populated maze is returned and used for the copy construction of a second maze object; the copy constructor will initialise this second object to the valid, useable and useful state of the returned maze.

In short: There are two maze objects being constructed, their constuctors both handle initialisation to a valid state and the CreateRandomPerfectMaze function also does some mutation on that state.


I'm afraid I don't understand where the copy constructor comes in. If CreateRandomPerfectMaze returns a pointer or reference to what it has created, then surely there's no need for a copy constructor?

Quote:
Quote:Another more generic example: I've got a class and I want to load its state from an XML file. I call a default c'tor which puts it in a well-defined, initialized, but largely useless state, then I read in the XML file and set the class's properties appropriately. Same questions as above.
It depends what you mean by 'useless'; if the state legitimately has no use then there is no reason for you to ever invoke the constructor that initialises to that state in the first place - it's just like any other function, if you had a function that produced a meaningless result then why would you ever wish to call it? Why would you ever even write such a function?

The natural thing to do would be to load the state from the XML file and then create the object. Or have the object's constructor load it's own state from the file using some kind of abstraction (you probably wouldn't want the object to deal directly with the file).


Well, from what I understand, the process I described is idiomatic XML reading. Then again, I am extremely inexperienced with XML. I do remember reading articles on XML that stated that any object you load from XML should have a blank constructor and let you write all their properties (the article was for C#, but I think it applies here too) after the load. But either way, you've cleared up the actual questions I had, so thanks!
Quote:Original post by Ezbez
Sure, I just said already full, since the method I have worked with in the past to generate random, perfect mazes worked by removing walls rather than adding them.
Yeah, I mean, at least it has sense to it. I still feel a blank maze is perhaps more natural, but that's my taste.

Quote:I'm afraid I don't understand where the copy constructor comes in. If CreateRandomPerfectMaze returns a pointer or reference to what it has created, then surely there's no need for a copy constructor?
Your right, if the function doesn't return by value then you won't need a copy constructor. Hopefully in this case you'd be using RAII pointers. I'm not sure why you would return by pointer though?

Quote:Well, from what I understand, the process I described is idiomatic XML reading. Then again, I am extremely inexperienced with XML. I do remember reading articles on XML that stated that any object you load from XML should have a blank constructor and let you write all their properties (the article was for C#, but I think it applies here too) after the load.
Hmm, well C# supports XML as part of the language's standard library whereas C++ unfortunately has no equivalent support for XML. What would be considered idiomatic XML parsing in C++ would be using Boost's serialisation library and doesn't mandate the use of blank constructors. It is common to load to an object that is already constructed but I would still expect that object to exist in some valid state initially.
Quote:Original post by Gage64
The disadvantage of the latter is that if you need to delay the creation of the object or you need to be able to recreate the object (that is, destroy it and create it with a different state), you'll need to use dynamic memory allocation


Not true.

// Perfectly ok C++// ... in a function somewhere...int x = calculateSomething();int y = calculateSomethingElse();doMoreCalculations(x, y);doEvenMoreCalculations(y, x);Object obj(x, y); // zomg! It's allocated normally on the stack, and its creation// was delayed until after we figured out everything we needed to know.obj = Object(x + y, y); // Yep, we can recreate it, too, by simply constructing// from scratch, and assigning the scratch version to the existing variable.


Of course, you need to make sure your copy constructors and assignment operators (and destructors) will work properly. But that's important anyway.

This topic is closed to new replies.

Advertisement