Creating copies of pointers to objects

Pages: 12
Feb 22, 2011 at 2:57pm
Hey There.

I am trying to create a class called "enemyManager", that well, manages the enemies in my game (deployment, memory deletion, etc.).

Well, for the function that adds an "enemy" class to the manager, I'm having difficulty trying to create a copy of "refEnemy", then putting that copy into an array that manages the enemy data.

Here is the function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Add an enemy to the manager
void enemyManager::addEnemy(enemy *refEnemy, int instances) {
	if ((instances < 1) || (_numEnemies >= EM_MAX_NUM_ENEMIES))	// Just to makesure we don't add any extra stuff
		return;

	int tempVerticesArray[64][2];

	for (int i = 0; i < instances; i++) {
		enemies[_curAvailableSlot]->enemyPtr = refEnemy;	// <-- Issue is right here
		enemies[_curAvailableSlot]->ptrClearedFromMem = false;

		_curAvailableSlot++;
		_numEnemies++;
	}
}


All I know is that I'm putting the address of "refEnemy" into "enemyPtr" right now which is what I don't want. I want it to copy all the data from "refEnemy" to "enemyPtr".

If needed I'll provide the whole header file.

Thanks.
Feb 22, 2011 at 3:27pm
If enemy isn't abstract, enemies[_curAvailableSlot]->enemyPtr = new enemy(*refEnemy);
Feb 22, 2011 at 3:58pm
Wow, thanks, that worked out perfect.
Feb 22, 2011 at 7:07pm
But then you are losing the polymorphism
Wouldn't be better something like
1
2
3
class enemy{
  virtual enemy* clone() = 0;
};
Feb 22, 2011 at 7:46pm
Yes, hamsterman's example will compile, but will not work in practice. The clone pattern is necessary.
Feb 22, 2011 at 8:22pm
ne555 and jsmith, could you elaborate on this please? (Specifically what I'll need to do to copy all the data into "enemyPtr" properly.)

Also, lets say that I have a class called "boss":
1
2
3
4
5
6
7
8
9
class boss : public enemy {
public:
    boss();
    ~boss();

    doStuff();
private:
    _ID;
};


Would I be able to add it to the enemyManger as of right now? Like this:
1
2
3
enemyManager *em = new enemyManager();
boss *b1 = new boss();
em->addEnemy(b1);


Would I need to overload the function at all?
Feb 22, 2011 at 9:42pm
Consider this code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct Base {
    virtual ~Base() {}
    int x;
    int y;
};

struct Derived : Base {
    int z;
};

void copy_it( Base* b )
{
    Base* copy_of_it = new Base( b );
}

int main()
{
    Derived* d = new Derived;
    copy_it( d );
}


copy_it does what is called "slicing" -- an instance of Derived has two "layers" -- the bottom-most layer is the data members of Base and the topmost layer is the data members of Derived. When copy_it is called, it makes a copy not of the two-layer cake, but only of the bottom-most layer. This is the problem with polymorphism. To solve that problem -- to have copy_it make a complete copy of what is passed to it -- you have to write a virtual clone() method that derived instances must override.
Feb 22, 2011 at 10:38pm
So what would I want to include in my virtual clone() method?

And wouldn't that example code (if compiled and ran) create a memory leak?

EDIT:

I forgot to mention that the "enemy" class contains pointers to a lot of other classes (like "sprite", "sound", etc). So would I want the clone function/method to return clones of these other classes?
Last edited on Feb 22, 2011 at 11:02pm
Feb 22, 2011 at 11:10pm
The clone() method is quite simple, see below.

Yes, I did not delete copy_of_it, so it would leak. Fixed below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct Base {
    virtual ~Base() {}
    virtual Base* clone() const { return new Base( *this ); }
    int x;
    int y;
};

struct Derived : Base {
    virtual Derived* clone() const { return new Derived( *this ); }
    int z;
};

void copy_it( Base* b )
{
    Base* copy_of_it = b->clone();
    // use copy_of_it ...
    delete copy_of_it;
}

int main()
{
    Derived* d = new Derived;
    copy_it( d );
}


Whether you want to implement a deep copy or a shallow copy is up to your design. If the enemy class owns the pointers, then probably yes, you'll want a deep copy. If sprite and sound are also base classes, you'll need a clone method on them too. If they are not polymorphic, then you don't need the clone() method.

The clone() pattern exists solely to get around the slicing issue that results from casting a pointer up to a base class type and then attempting to copy the entire object through the base class pointer.
Feb 23, 2011 at 5:18am
Well for my game, the basic variable structure of "enemy" is this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class enemy : public sprite {
public:
	static int sNextEnemyID;

	bool invul;
	int speed;
	int damage;
	int pointValue;

private:
	int _enemyID;
	int _curHealth;
	int _maxHealth;
	bool _isAlive;
	bool _hitNoiseAdded;
	bool _deathNoiseAdded;
	bool _deathSpriteAdded;
	sound *_hitNoise;
	sound *_deathNoise;
	sprite *_deathSprite;
	int _deathSpriteOffsetX;
	int _deathSpriteOffsetY;
	int _deathSpriteTimer;
	bool _deathRoutineStarted;

};


Sprite and Sound are base classes, but as you can see, "enemy" is derived from "sprite".

I'm also using The DarkGDK game library, and each time I create a new sprite, it calls a DarkGDK function where it loads an image file into memory and then creates a sprite for it. Both of the base classes don't contain any pointers.

So I'm guessing a deep copy here?
Last edited on Feb 23, 2011 at 5:21am
Feb 23, 2011 at 5:50am
Who is responsible for freeing the memory allocated to _hitNoise, _deathNoise, and _deathSprite?

If it is the enemy class, then yes, you'll need a deep copy.
Feb 23, 2011 at 3:08pm
In the "enemy" class deconstructer, it uses the "delete" keyword.

Ex:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Enemy class destructor
// cleans up some of the stuff for us
enemy::~enemy() {
	// Delete the hit noise
	if (_hitNoiseAdded)
		delete _hitNoise;

	// Delete the death noise
	if (_deathNoiseAdded)
		delete _deathNoise;

	// Delete the death Sprite
	if (_deathSpriteAdded)
		delete _deathSprite;
}
Feb 28, 2011 at 3:58am
Okay, sorry for both double-posting and necro-posting.

I got my "clone()" method written for both my "sprite" and "enemy" class, but I'm running into a little bit of a problem.

I'm getting these compiler errors:
1
2
3
4
5
error C2143: syntax error : missing ';' before '*'
error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
error C2556: 'int *enemy::clone(void)' : overloaded function differs only by return type from 'enemy *enemy::clone(void)' see declaration of 'enemy::clone'
error C2371: 'enemy::clone' : redefinition; different basic types see declaration of 'enemy::clone'


I think it has something to do with my function declarations and definitions.

For "sprite" class: (<- This works weel by itself.)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class sprite {
    public:
        // ...

	virtual sprite *clone();

        // ...
};

sprite *sprite::clone() {
    sprite *cpy = new sprite();

    // ...

    return cpy;
}


For "enemy" class: (<- Errors upon compiling.)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class enemy : public sprite {
    public:
        // ..

        virtual enemy *clone();

        // ..
};


enemey *enemy::clone() {
    enemy *cpy = new enemy();

    // ...

    return cpy;
}


Any help is really appreciated.

Thanks.
Last edited on Feb 28, 2011 at 4:00am
Feb 28, 2011 at 4:27am
enemey *enemy::clone() { :)
Feb 28, 2011 at 1:07pm
The clone method is supposed to return a pointer to the base class (in this case sprite). You can't modify that prototype.

However it seems a little weird to say that an enemy is a sprite. Wouldn't be better to say that enemy has a sprite?

Edit: sorry about the misinformation.
Thanks for the correction.
Last edited on Feb 28, 2011 at 5:22pm
Feb 28, 2011 at 2:19pm
No, you can modify the prototype. Remember that the return type of the function is not part of the function's type, so that

1
2
3
4
5
6
7
struct Base{
    virtual int frobnicate() = 0;
};

struct Derived : base {
    virtual float frobnicate() {}
};


makes Derived a concrete class since Derived's frobnicate() is an override of Base's pure virtual method. It's perfectly ok for the derived class therefore to return a pointer to its type instead of the base type.
Feb 28, 2011 at 5:01pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct Base{
	virtual int frobnicate() = 0; //error: overriding 'virtual int Base::frobnicate()'
};

struct Derived : public Base {
	virtual float frobnicate() {} //error: conflicting return type specified for 'virtual float Derived::frobnicate()'
};

int main(int argc, char *argv[]) {
	Base *b;
	b = new Derived;
	b->frobnicate();
	return 0;
}
Feb 28, 2011 at 7:03pm
Sorry, doesn't like the int/float thing, but replacing int with Base* and float with Derived*, it compiles fine.
Feb 28, 2011 at 7:12pm
Sorry, my bad.

If you replace "int" with "Base*" and "float" with "Derived*" (then return 0) it compiles fine.
Mar 1, 2011 at 2:17am
@simeonz
Thanks. (Wow I feel like and idiot.)

@Ne555
The reason why I derived "enemy" from "sprite" is that the original class for "enemy" contained many similar things that "sprite" had, so why not just derive it. Lot of work saved.

Thanks everyone.
Pages: 12