Pointer Question

Started by
7 comments, last by Nyarlath 15 years, 5 months ago
I'm creating a Tetris game using C++ and SDL, but I'm having a bit of trouble with getting some of my code to work. I have a TetrisBlock class that serves as a base class for the the different shapes (Tshape, Line, LLeft, Lright and Odd ) so that they can redefine how they're created and how they rotate. I also have a BlockLogic class that uses a TetrisBlock* as a representation of the block that the user is controlling. BlockLogic has a CreateBlock method that accepts a TetrisBlock& to initialize the TetrisBlock* So, my question is, is there a way to determine what type was passed to CreateBlock and recreate it in BlockLogic? Here's a snippet of my code with commented intentions.

//TSHAPE	userBlock;
//BlockLogic	logic;
//logic.InitBlock( userBlock );

void BlockLogic::InitBlock( const TetrisBlock& newBlock )
{
	delete mTestBlock;
	mTestBlock = new TetrisBlock( newBlock ); //copy info from newBlock

	BlockPosition origin = BlockPosition( ARRAY_CENTER, ARRAY_TOP - 1);

	//I want this to call TSHAPE's create method, not TetrisBlock's
	mTestBlock->Create( origin ); 
	
	SetBlock( ); //modify tetris array
}
Advertisement
The general answer for this is: look into 'virtual functions'. They allow a function to be resolved according to the run-time type of the object.

Now, in particular, I think you are overenginnering this. Don't create a new class for every single slight difference you see. Many times the simplest solution is the optimal. A single TetrisBlock class representing a rotating 4x4 grid would be more than enough.
yeah, does each block type have its own inherited class definition? Why, are they going to do different, un-template-able things?
I know a simple solution would be for BlockLogic to know all of the different shapes there are and create one depending on what the user wants to create, but I'm trying to wrap my head around the different limitations of abstracting concepts. In this case, I want BlockLogic to be able to create objects derived from TetrisBlock, but I'm not sure if it can do that without knowing exactly which object to create.

class TetrisBlock{	/* snip */	virtual void Create( const BlockPosition& );};class TSHAPE : public TetrisBlock{	virtual void Create( const BlockPosition& );};

Create is a virtual function, but since BlockLogic has no information about the TSHAPE class, I'm not sure if it can create a TSHAPE to call its Create function.
Then what you want to do is to (modify and) call newBlock's Create function:

class TetrisBlock{	/* snip */	virtual TetrisBlock* Create( const BlockPosition& );};class TSHAPE : public TetrisBlock{	virtual TetrisBlock* Create( const BlockPosition& );};void BlockLogic::InitBlock( const TetrisBlock& newBlock ){	delete mTestBlock;	BlockPosition origin = BlockPosition( ARRAY_CENTER, ARRAY_TOP - 1);	mTestBlock = newBlock.Create(origin);  // Create() now creates a copy of the object and returns it.	SetBlock( ); //modify tetris array}


Or call it CreateCopy() or something.
Quote:Original post by pLikT
I have a TetrisBlock class that serves as a base class for the the different shapes

Total overkill. For example, you wouldn't think of writing a new class for every possible character string, would you?
Then I can just have TSHAPE Create method return a new TSHAPE, or if I want to copy another TetrisBlock, return a new dynamic_cast< TSHAPE* > ( copyTetrisBlock ). That idea hadn't occured to me. Thanks. =)
Quote:Original post by DevFred
Total overkill. For example, you wouldn't think of writing a new class for every possible character string, would you?
I designed it like that because when I originally made my BlockLogic class, I didn't remember all of the different block shapes.

When I went back to add additional code for the different shapes, I realized I was changing and recompiling the code for block logic, which doesn't need to know that, the Square doesn't rotate, or how the T-shape is formed, it only needs to know what spots in the array the TetrisBlock is taking up. In the case of Tetris, the block shapes are all static, but applying the concept will help me for future projects. At least, that's how I see it.
Quote:Original post by pLikT
I originally made my BlockLogic class, I didn't remember all of the different block shapes.


?!

Quote:When I went back to add additional code for the different shapes, I realized I was changing and recompiling the code for block logic, which doesn't need to know that


Different shapes do not require any different block logic.

Quote:the Square doesn't rotate


The square does rotate. It just happens to look the same after rotation as before.

Quote:Original post by pLikT
Then I can just have TSHAPE Create method return a new TSHAPE, or if I want to copy another TetrisBlock, return a new dynamic_cast<TSHAPE*>( copyTetrisBlock ).

Be careful: You cannot simply cast any TSHAPE to any other.
What you write is wrong, you might want
return dynamic_cast<TSHAPE*>(copyTetrisBlock->Create());orreturn dynamic_cast<TSHAPE*>(copyTetrisBlock)->Create();
but both are wrong (if copyTetrisBlock is not a TSHAPE the first returns null and the second crashes). You can at most write a constructor TSHAPE(const TetrisBlock&) for each TSHAPE and then make the Create method return a new TSHAPE(copyTetrisBlock).

I agree with DevFred; I believe your model is much too complicated for that problem (different TSHAPES are not compatible: You really want that?!). Try to keep your design simple and effective (IMHO that part is of greater importance with respect to learning how to make complicated things work).
E.g. You really need a specific empty Rotate function for the square object, or you could simply implement a unique Rotate function which returns Next[shape], with Next[square] = square?

This topic is closed to new replies.

Advertisement