Skip to main content
GameDev.net gamedev.net
🔒 Locked

Questions about Deep Copying with Inheritance

Started by Nazrix Apr 1, 2007 at 12:30 AM 1 replies 1.3k views
Original Post
Nazrix
Nazrix
Goal.h - This is the base class for storing NPCs' goals

class Goal
{
private:
	Character *doer;
	Character *giver;
	//TODO --- Item rewards[3];
	//TODO --- NEED fulfilled;
	
public:
	MORALITY morality; //good, evil, etc

	

public:
	
	bool finished; //is it finished

        virtual void Execute()=0;
...



NPC.h


class NPC : public Character
{
public:
	Goal *currGoal;

};


TakeItem.h - This is a class that is a specific Goal for taking items off of the ground

class Goal;
class Move;

class TakeItem : public Goal
{
public:
	Item *item;
	Move move;

	

	TakeItem() {
		item=NULL;
		setDoer(NULL);
		finished=false;
	}

	

	void Execute ()
	{
	
		if (!move.finished)
		{
			move.Execute();	
			printf("%s moves to (%f,%f,%f)\n", getDoer()->getName(),getDoer()->x,getDoer()->y,getDoer()->z);	

			//if the npc just finished the move:
			if (move.finished)
			{
				item->personCarrying=getDoer();

				printf("\n%s takes item: <%s>\n\n",getDoer()->getName(), item->getName());	
			}

		}

		else 
			finished=true;
	}

};


Another file...

	TakeItem take;
	take.item=items[indexOfClosestItem];
	take.setDoer(&NPCs);

	NPCs.currGoal=&take


So in the last part is within a function I call from my main.cpp file. So basically the currGoal variable is a pointer so I can put any Goal I want there (like the TakeItem goal for instance) My question is do I need some sort of deep copy so currGoal can see all the pointers that are in the take variable? If so, I'm a bit confused about how to go about doing a deep copy. The inheritance is making it confusing for me. Thanks!
Need help? Well, go FAQ yourself. "Just don't look at the hole." -- Unspoken_Magi
Zahlman
Zahlman
First, some cleanup:

class Goal {	protected:	Character *doer;	Character *giver;	Morality morality; // good, evil, etc	virtual void executeImpl() = 0;	public:	Goal(Character* doer, Character* giver = 0) : doer(doer), giver(giver), finished(false) {}	// These should probably be read-only to the public, thus:	const Morality& getMorality() { return morality; }	virtual bool isFinished() = 0; // yes, really        void Execute() {		if (!isFinished()) { executeImpl(); }	}	// ...};class TakeItem : public Goal {	public:	Item *item;	Move move;	TakeItem(Character* doer, Character* giver, Item* item) : Goal(doer, giver), item(item) {}	virtual void isFinished() { return move.finished; }	void executeImpl() {		move.Execute();		// Why would you use printf()? :( Also, note that I am		// re-distributing the work here; the Character interface must		// change accordingly		cout << doer->name() << "moves to " << doer->position() << "\n";		if (move.finished) {			item->personCarrying = doer;			// Yes, it is possible to make this work with 'item' as			// is, and you probably should do so.			cout << "\n" << doer->name() << " takes item: " << item << "\n\n";		}	}};class NPC : public Character {	Goal *currGoal;	NPC(const NPC&); // do not implement!	NPC& operator=(const NPC&); // do not implement!	public:	NPC() : currGoal(0) {}	~NPC() { delete currGoal; }	void tryToTake(Item* i) {		delete currGoal;		currGoal = new TakeItem(this, 0, i);	}};NPCs.tryToTake(items[indexOfClosestItem]);


Quote:

My question is do I need some sort of deep copy so currGoal can see all the pointers that are in the take variable?


Well, currGoal was a pointer, and you were pointing it at an existing object, so there is no copy to make at all - the question was irrelevant. But this is a bad idea, because the TakeItem instance that you created would probably not outlive the NPC you assigned it to, and logically it should be "owned" by that NPC. Right now, our main reason for using a pointer at all is for polymorphism. So, what I have done is to set up the NPC class so that it "owns" a dynamically allocated Goal (or subclass thereof), and manages that memory. If we were to copy an *NPC* instance now, we would need to worry about the copy semantics, yes. But chances are we shouldn't be doing that, so I instead put in the code above to ensure that NPCs *can't* be copied (the compiler will complain if we try, whether accidentally or deliberately).

Were we to implement those functions, we would probably want the copied NPC to "clone" the Goal object - we cannot simply invoke the Goal copy constructor, because that will cause the copied NPC to point to an instance of the base class. Instead, what we would do is create a virtual member function of Goal called clone(), and implement it in each derived class, such that it invoked the copy constructor of the current derived class (the "virtual clone idiom"). Of course, this would again do a new allocation, so that the copied NPC would itself point at an "owned" allocation. This looks like:

class Goal {	// other stuff	public:	virtual Goal* clone() = 0;};class TakeItem {	// other stuff	public:	// clone() is implemented in terms of the copy constructor.	virtual TakeItem* clone() { return new TakeItem(*this); }	// Because a TakeItem object does NOT "own" the pointed at 'doer',	// 'giver' or 'item', but simply "knows about" pre-existing objects via	// those pointers, it is correct for the copy constructor to just copy	// the pointer values. Therefore, as long as the Move copy constructor	// works properly, there is no need to implement a copy constructor	// for TakeItem at all; the default generated one will work.};class NPC : public Character {	Goal *currGoal;	public:	// We ask the 'other' NPC's goal to clone itself, and then initialize	// our goal to be the cloned object.	NPC(const NPC& other) : currGoal(other.currGoal->clone()) {}			NPC& operator=(const NPC& rhs) {		// We use the copy and swap idiom.		NPC other(rhs);		std::swap(currGoal, other.currGoal);		return *this;		// because of the swap, the destructor of 'other' will		// automatically clean up our old Goal, instead of the cloned		// one in the 'other' NPC (which is now our Goal).	}};
Nazrix
Nazrix
Quote:
But this is a bad idea, because the TakeItem instance that you created would probably not outlive the NPC you assigned it to,




Yes, this was my problem exactly.


Thank you so much. This is exactly what I needed :):)
Need help? Well, go FAQ yourself. "Just don't look at the hole." -- Unspoken_Magi

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.