|
theman515
|
 |
« on: December 22, 2011, 01:56:29 AM » |
|
Hey guys, I'm writing a console for my side-scrolling shoot-em-up and I'm currently writing a command to change certain variables of a specific object. the command goes through a list of objects that are all derived from the same class but they themselves are different classes and assigns the correct object to a pointer. Now I run into a problem, currently my code looks like so: if(refID == Lance::Ref) Lance obj*; if(refID == Floor::Ref) Floor obj*;
which is fine now when I only have 2 test classes but when I start adding objects into the universe the chance for error and forgetting something becomes really high. My question is: is there an easier way to do what I'm doing? Thanks in advance 
|
|
|
|
|
Logged
|
|
|
|
|
Netsu
|
 |
« Reply #1 on: December 22, 2011, 02:26:14 AM » |
|
As long as this is C++: I think a more elegant solution would be to always create a pointer to the base class from which all those are derived, and assign the object to this pointer.
|
|
|
|
|
Logged
|
|
|
|
|
bauer
|
 |
« Reply #2 on: December 22, 2011, 02:50:25 AM » |
|
How are your objects structured? You say that they're all derived from the same base class? Does each object have a unique ID or is the ID unique per object type (per class basically)?
I also agree that this sounds like a problem best solved by a general interface using pointers to the base class and virtual methods.
|
|
|
|
|
Logged
|
|
|
|
|
theman515
|
 |
« Reply #3 on: December 22, 2011, 01:14:27 PM » |
|
Netsu: I tried that, and it works for any variable inherited by the superclass but there are some variables unique to each child class that i need to change and yes,this is C++.
bauer: they all come from a class named IScreenElement and have 3 virtual functions: render(), onFrame(), and init(). each child class has a refID which is unique to each class type and a uniqueID which is unique to each individual object and gets created when the object initializes.
I could just make the specific functions that edit the variables virtual functions but seeing as I would only use these functions for one class I figured it would be a huge waste of resources.
|
|
|
|
|
Logged
|
|
|
|
|
Netsu
|
 |
« Reply #4 on: December 22, 2011, 01:18:10 PM » |
|
Netsu: I tried that, and it works for any variable inherited by the superclass but there are some variables unique to each child class that i need to change and yes,this is C++.
You can always cast the pointer to the correct type when the need comes to access the unique variables/methods.
|
|
|
|
|
Logged
|
|
|
|
|
theman515
|
 |
« Reply #5 on: December 22, 2011, 01:21:08 PM » |
|
like this (type)variable->function()?
|
|
|
|
|
Logged
|
|
|
|
|
Netsu
|
 |
« Reply #6 on: December 22, 2011, 01:24:19 PM » |
|
Yes, although you might need additional parentheses, I'm not sure, in your case it would look like this: ((Lance*)obj)->function();
|
|
|
|
|
Logged
|
|
|
|
|
theman515
|
 |
« Reply #7 on: December 22, 2011, 01:40:48 PM » |
|
Works wonderfully, thanks Netsu 
|
|
|
|
|
Logged
|
|
|
|
|
Netsu
|
 |
« Reply #8 on: December 22, 2011, 01:45:34 PM » |
|
Not a problem 
|
|
|
|
|
Logged
|
|
|
|
|
BlueSweatshirt
|
 |
« Reply #9 on: December 22, 2011, 10:38:51 PM » |
|
Couldn't you reduce all this bloat by making function() virtual? Like, even if the variables being changed are different you could just pass a generic string into function() to generalize it. It's easy enough to pull a number out of a string. Or is there something I'm missing? 
|
|
|
|
|
Logged
|
|
|
|
|
Netsu
|
 |
« Reply #10 on: December 23, 2011, 01:15:54 AM » |
|
That's also neat, I don't know which is considered 'better practice'.
|
|
|
|
|
Logged
|
|
|
|
|
BlueSweatshirt
|
 |
« Reply #11 on: December 23, 2011, 02:26:34 AM » |
|
Well I think virtual is the 'better practice' because it removes the need for him to write that redundant, lengthy code to transform an object for each case. It would make implementing new objects easier and his code would have less bloat.
Of course, this is assuming he's trying to editing data that can be represented by text easily enough. I assume this is for a debug console or something.
If he is looking to do something more complicated like passing image data he could always encode it, at the loss of time of course. The other option would be to take the often-shunned-but-equally-effective function(void* param) route and cast it as your desired data type inside of your function implementation.
|
|
|
|
|
Logged
|
|
|
|
|
Klaim
|
 |
« Reply #12 on: December 23, 2011, 02:29:15 AM » |
|
Obviously, using virtual is the good practice. That's precisely the kind of case why they exist and one of the goals is to avoid totally casts.
Because casts are problematic. In your suggestion you're casting someting into something else without knowing if it's the right type, not checked at compile-time or run-time, so the day you pass something that you didn't manage in this code you'll enter "undefined behaviour" land.
Also, if you're forced to use a cast, then use C++ casts, not C cast because C cast is not safe at all. C++ casts does each one and only one thing and are ugly enough to be noticed by the user easily, or searched in the code base. static_cast only convert at compile-time a pointer or reference into the type hierarchy. dynamic_cast does the same but at runtime and if you use a reference it will throw an exception, if you use a pointer you can get a nullptr pointer meaning the cast failed (but it obviously have a cost on execution speed). const_cast allow switching from const to non-const and vice-versa, and reinterpret_cast tells the compiler to do "whatever it can" to cast, almost the same as C cast but at least you have an obvious point in the code to see what the hell you were doing at that moment.
So, for the simplification : have a common interface with a virtual function.
IF you want to plug functions or functors only (if you don't need to have a specific full interface), you could also manage std::function objects, that would make the abstraction with "anything that can be called with ()".
If you can, don't use casts. Casts are for those ugly cases where you need to dirt your hands with raw memory or when you need to manage data from other binaries that you have to make the compiler make sure it's of the wanted types.
|
|
|
|
|
Logged
|
|
|
|
|
Netsu
|
 |
« Reply #13 on: December 23, 2011, 02:33:57 AM » |
|
Thanks guys, that's good to know  Should I always have one virtual method that contains all the custom behaviour for each sub class or do something else if I need more than one logical functionality added?
|
|
|
|
|
Logged
|
|
|
|
|
BorisTheBrave
|
 |
« Reply #14 on: December 23, 2011, 04:33:17 AM » |
|
You should have one virtual method for each logically distinct behaviour, just like regular methods.
The Visitor pattern may also be handy to you - it's the equivalent of virtual methods, except you don't have to specify the methods with the classes.
Also possible for your specific case is reflection (which requires work to hack into C++). But I'd go with virtual methods until you find they are cluttering too much.
|
|
|
|
|
Logged
|
|
|
|
|