Initializing a C++ class with a reference to another class

I’ve been working on a small game that has a Player class and a Render class. The Render class needs to know about the Player class so that it can draw the player in the right spot. My first pass at this had a method in the Render class called UpdatePlayerLocation where I passed it the Player’s location.

This seemed a bit inefficient. I figured it would be better if my Render class was initialized with a reference to my (existing) Player class, so that any time the Player location was updated my Render class would already know about it.

All of my attempts to get this to work resulted in a compiler error, or the Render class only creating a local copy of the Player class, meaning my updates to the Player outside of Render were ignored by Render.

I decided to create a small program to test my methods and figured it out. It’s extremely troubling how I got it to work. Let me demonstrate the wrong way (only a local copy created), the error way, and the right way.

The general idea is that I have a class called SomeNumber that is initialized with a number, and has methods for getting and setting this number after initialization. I have another class called NumberPrinter that is initialized with knowledge of an instance of the SomeNumber class. NumberPrinter prints the number stored in the instance of SomeNumber class that it’s passed. The program(s) below show the definitions of the SomeNumber and NumberPrinter classes, and the main() function initializes the classes, lets them print their number values, changes the SomeNumber number, and prints again. Ideally the number change in SomeNumber will propagate to NumberPrinter (propagate is the wrong word since it’s just a reference, but you know what I mean).

The Wrong Way

#include "stdafx.h"

class SomeNumber
{
public:
	SomeNumber(){};
	SomeNumber(int number)
	{
		mNumber = number;
	}

	void SetNumber(int number)
	{
		mNumber = number;
	}

	int GetNumber()
	{
		return mNumber;
	}
	void PrintNumber()
	{
		printf("In SomeNumber: %i\n", mNumber);
	}

private:
	int mNumber;
};

class NumberPrinter
{
public:
	NumberPrinter(SomeNumber& somenumber)
	{
		mSomeNumber = somenumber;
	}

	void PrintNumber()
	{
		printf("In NumberPrinter: %i\n", mSomeNumber.GetNumber());
	}

private:
	SomeNumber mSomeNumber;
};

int _tmain(int argc, _TCHAR* argv[])
{
	SomeNumber somenumber(42);
	NumberPrinter numberprinter(somenumber);

	somenumber.PrintNumber();
	numberprinter.PrintNumber();

	printf("\nSetting SomeNumber to 27\n\n");
	somenumber.SetNumber(27);

	somenumber.PrintNumber();
	numberprinter.PrintNumber();

	getchar();
	return 0;
}

This was the wrong way. The output is:

wrongway

The second printout of SomeNumber and NumberPrinter doesn’t match. This is because the class definition of NumberPrinter is wrong. It looks like it’s being initialized with a reference to a SomeNumber class instance, but it seems that setting a local (non-reference type) instance of SomeNumber to this passed reference only creates a class-local copy. mSomeNumber becomes a brand new instance (copy?), and isn’t affected by anything done outside of NumberPrinter. This seems obvious in hindsight.

So I figured that I should simply not declare my private mSomeNumber variable to be a SomeNumber class, but a reference to a SomeNumber class. This leads me to the error way.

The Error Way

I won’t copy/paste the entire program. Only the NumberPrinter class changed.

class NumberPrinter
{
public:
	NumberPrinter(SomeNumber& somenumber)
	{
		mSomeNumber = somenumber;
	}

	void PrintNumber()
	{
		printf("In NumberPrinter: %i\n", mSomeNumber.GetNumber());
	}

private:
	SomeNumber& mSomeNumber;
};

This results in a compiler error. “a member of reference type must be initialized”. I Googled the error, then refined my search to be more about how to pass a class a class reference in the constructor and came across this StackOverflow post: http://stackoverflow.com/questions/11117347/passing-references-to-a-c-constructor-and-saving-them-to-reference-or-non-refe

The guy in the post wasn’t getting the same error (he was asking a different question entirely) but I noticed his code worked and he was doing pretty much the same thing I was doing. The only difference was how he was doing the initializing. This leads me to the right way.

The Right Way

class NumberPrinter
{
public:
	NumberPrinter(SomeNumber& somenumber)
		:mSomeNumber(somenumber)
	{

	}

	void PrintNumber()
	{
		printf("In NumberPrinter: %i\n", mSomeNumber.GetNumber());
	}

private:
	SomeNumber& mSomeNumber;
};

Instead of putting mSomeNumber = somenumber in the constructor function body, I used the other way if initializing class variables with :mSomeNumber(somenumber). Here’s the results:

therightway

 

Yes, this is what I wanted!

I sincerely thought that initializing class variables like that was a style choice, but it seems to be a rule for some cases.

I’m pretty new to C++, and stuff like this eats up entire evenings. Thankfully I live in a time when StackOverflow exists. I’d have been super stuck not realizing that the word “initialized” can have different contextual meanings! I think that the colon marks the start of an “initializer list”.

I read a few more posts on the subject. I can kind of see why this is. You don’t want to pass a reference that could potentially get freed outside of the class it’s passed to. I wonder if there is a graceful “smart pointer” way of handling it, or maybe I could just use raw pointers. More experimenting is needed. Here is another SO post that I found interesting: http://stackoverflow.com/questions/17992121/must-a-reference-type-be-initialized-in-constructor-initialization-list

 

 

This entry was posted in Programming. Bookmark the permalink.

One Response to Initializing a C++ class with a reference to another class

  1. Anonymous says:

    It’s not really the initialization that makes the varaiable a reference, but rather its declaration – notice how in the first code you used SomeNumber mSomeNumber; and in the final one SomeNumber& mSomeNumber; (note the ampersand).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s