Thread: Most Efficient Way to Do This?

  1. #1
    Registered User
    Join Date
    May 2011
    Posts
    30

    Most Efficient Way to Do This?

    I'm creating a game. I have a base Image class, with derived Decal and Primitive classes. I also have a seperate class named Frame that holds all Decals and Primitives. What's the best way to go about doing this? I originally thought about making dynamic arrays within the Frame for each base and derived class. For example:

    Code:
    class Frame{
    //vector for Decals
    //vector for Primitives
    };
    However, I thought that this was very inefficient. I want to have a single member method in the Frame class that could take Decals, Primitives, and any new image types I may want to create. I figured a template function would be best for this. However, I'd have to do a specialization for each and every applicable type, as each one would be stored in a different array. Would I be better off doing this mess, or should I cast all of the derived ImageObjects into the base, and then store that in a dynamic array? Problem is, converting them back is never a good idea... ugh, can anyone help me out here? Is there something I'm missing?

  2. #2
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by Shokwav View Post
    I want to have a single member method in the Frame class that could take Decals, Primitives, and any new image types I may want to create.
    Why? Wouldn't it be better to have a set of overloaded methods? You could then have an internal private method that takes the base Image class as a param including whatever code is common to all the possible derived classes, and call that with a cast:

    Code:
    class Frame {
    	private:
    		void processImage (Image *img) {
    			// stuff common to all to all Image derived types
    		}
    	public:
    		void process (Decal *dec) {
    			processImage ((Image*)dec);
    			// stuff particular to Decals
    		}
    		void process (Primitive *prim) {
    			processImage ((Image*)prim);
    			// stuff particular to Primitives 
    		}
    };
    No templates required.
    Last edited by MK27; 03-02-2012 at 11:46 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  3. #3
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    You don't even need a cast since a Decal is an Image and a Primitive is an Image.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  4. #4
    Registered User
    Join Date
    May 2011
    Posts
    30
    Thank you, that really helps me; I can just add an extra overloaded method for any new types. However, for the array bit, do you think I should cast them to the base class to be stored in array? The problem is, I need to access specific properties specific to each derivative when they are retrieved.

    Again, thank you for your help.

  5. #5
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Shokwav
    However, for the array bit, do you think I should cast them to the base class to be stored in array? The problem is, I need to access specific properties specific to each derivative.
    You should store a vector of (smart) pointers. If you store a vector of Image objects, there is type slicing and you lose the parts specific to each derived class.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  6. #6
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by laserlight View Post
    You don't even need a cast since a Decal is an Image and a Primitive is an Image.
    Hmm, okay. In theory I would think that is how it should be, but I get "error: ‘Image’ is an inaccessible base of ‘Primitive’" when I try to compile the below [subsequently corrected] without the cast. I should set myself out some simple C++ specific examples -- otherwise I tend to use a quick hit and miss to deduce what rules apply in which language when what I think "in theory should be" goes wrong.

    @Shokwav WRT to accessing arrays, you can pass a reference to them internally too:

    Code:
    #include <vector>
    
    using namespace std;
    
    class Image {
    	public:
    		virtual ~Image () {}
    };
    
    class Decal : public Image {};
    
    class Primitive : public Image {};
    
    class Frame {
    	private:
    		vector<Decal*> decals;
    		vector<Primitive*> primitives;
    		void processImage (Image *img, vector<Image*> &list) {
    			list.push_back(img);
    			// other stuff common to all to all Image derived types
    		}
    	public:
    		void process (Decal *dec) {
    			processImage (dec, (vector<Image*>&)decals);
    			// stuff particular to Decals
    		}
    		void process (Primitive *prim) {
    			processImage (prim, (vector<Image*>&)primitives);
    			// stuff particular to Primitives 
    		}
    };
    
    int main(void) {
    	Decal *d = new Decal();
    	Primitive *p = new Primitive();
    	Frame *f = new Frame();
    	f->process(p);
    	f->process(d);
    	return 0;
    }
    If laserlight can straighten out the casting issue for me, I now have a C++ specific example of...something...

    Quote Originally Posted by laserlight View Post
    You should store a vector of (smart) pointers. If you store a vector of Image objects, there is type slicing and you lose the parts specific to each derived class.
    I think my idea here gets around this, since the vectors are correctly typed.
    Last edited by MK27; 03-02-2012 at 12:45 PM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  7. #7
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by MK27
    In theory I would think that is how it should be, but I get "error: ‘Image’ is an inaccessible base of ‘Primitive’" when I try to compile the below without the cast.
    That is because you used private inheritance. It should have been:
    Code:
    class Image {
        public:
            virtual ~Image() {}
    };
     
    class Decal : public Image {};
     
    class Primitive : public Image {};
    Quote Originally Posted by MK27
    I think my idea here gets around this, since the vectors are correctly typed.
    Oh yeah, plus just passing a pointer to Image to process would otherwise invoke the version for Image (we aren't really using the visitor pattern here, methinks).
    Last edited by laserlight; 03-02-2012 at 12:16 PM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  8. #8
    Registered User
    Join Date
    May 2011
    Posts
    30
    Ah, so I do have to create multiple vectors to avoid the data loss of upcasting? Okay, that's what I thought. :P Thank both of you, this has answered my question; your example is also relatively simple and not overty complex, MK27.

    EDIT: Got my code completed, no errors.

    #include <vector>
    #include "Outstream.h"
    #include "ImageObject.h"

    class Frame{

    std::vector<Decal*> DecalCntr;
    std::vector<Primitive*> PrimCntr;

    void AddObject(ImageObject* ImgObj, std::vector<ImageObject*> &Cntr){
    Cntr.push_back(ImgObj);
    }

    public:

    Frame();
    ~Frame();

    void AddObject(Decal* Dcl){
    AddObject((ImageObject*)Dcl, (std::vector<ImageObject*>&)DecalCntr);
    Print(Dcl->GetImgSrc());
    }

    void AddObject(Primitive* Prim){
    AddObject((ImageObject*)Prim, (std::vector<ImageObject*>&)PrimCntr);
    }
    };
    Last edited by Shokwav; 03-02-2012 at 12:26 PM.

  9. #9
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by laserlight View Post
    just passing a pointer to Image to process
    Whoops! I actually think it was then not such a good idea to dereference those for the vectors, so I corrected the code in post 6 to use vector<*> (and public access to the base). I guess the pointer issue all hinges on whether the Decals and Primitives have a life beyond the Frame they are submitted to; if not, then there should be no pointers and just references in that code. Squiggles & vs. quips * .

    the visitor pattern
    Will look that one up .
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  10. #10
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Shokwav
    Ah, so I do have to create multiple vectors to avoid the data loss of upcasting?
    No, you don't. The data loss comes when you cast a Decal to an Image. It does not happen when you cast a Decal* to an Image*, and in fact you don't need a cast at all for that: it is normal use of polymorphism.

    The problem is that once you only have a vector of Image pointers, you are constrained to the Image interface, i.e., if you want to do something special for objects of a derived class that is not provided for in a virtual member function of Image, then you need to use say, dynamic_cast.

    One way out is to apply the visitor method: create an ImageVisitor class that provides a virtual visitX member function corresponding to each Image derived class X. Derive Frame from ImageVisitor and implement these visit functions. Image has a virtual accept member function that takes an ImageVisitor object by reference, then each derived class X overrides accept to call the appropriate visitX member function of the ImageVisitor. The drawback is that if you add a new class derived from Image, ImageVisitor and all its derived classes should be updated to handle this new class.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  11. #11
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by laserlight View Post
    One way out is to apply the visitor method:
    I did this as exercise for my hackish C++, but I did in reverse of your suggestion (such that the image types visit the frame):

    Code:
    #include <vector>
    #include <iostream>
    #include <string>
    
    class Object {
    		std::string name;
    	public:
    		void setName (std::string name) { this->name = name; }
    		const std::string getName() { return name; }
    };
    
    class Frame;
    class Image;
    
    class Frame : public Object{
    	private:
    		std::vector<Image*> images;
    	public:
    		Frame (std::string name) {
    			setName(name);
    		}
    		void accept (Image*);
    };
    
    class Image : public Object {
    	public:
    		void visit(Frame* f) {
    			std::cout << getName() << ": " << f->getName() << std::endl;
    		}
    		~Image () {} 
    };
    
    class Decal : public Image {
    	public:
    		Decal () { setName("Decal"); }
    };
    
    class Primitive : public Image {
    	public:
    		Primitive () { setName("Primitive"); }
    };
    
    void Frame::accept (Image *img) {
    	img->visit(this);
    	images.push_back(img);
    }
    
    int main (void) {
    	Frame f = Frame("Frame 1");
    	f.accept(new Decal());
    	f.accept(new Primitive());
    	return 0;
    }
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Efficient TSP
    By Cpro in forum C++ Programming
    Replies: 2
    Last Post: 09-10-2008, 08:52 PM
  2. is mod(%) efficient?
    By franziss in forum C++ Programming
    Replies: 7
    Last Post: 05-14-2007, 09:46 AM
  3. CPU efficient
    By Queatrix in forum Game Programming
    Replies: 8
    Last Post: 12-15-2006, 02:20 AM
  4. Which of these is more efficient?
    By suzakugaiden in forum C++ Programming
    Replies: 8
    Last Post: 02-06-2006, 11:35 PM
  5. New but efficient way to...
    By jmd15 in forum C++ Programming
    Replies: 5
    Last Post: 11-01-2005, 04:19 PM