Sign in to follow this  
Raeldor

Best Way to Pass by Refence in C++

Recommended Posts

For functions in classes, what is the best way to pass by reference? I assume it is good to keep consistency in applications, so I was wondering should I use void Test(Tile &InTile) { } or void Test(Tile *pInTile) { } I know I am probably being a bit fussy, but I would like to get this right from the start.

Share this post


Link to post
Share on other sites
I prefer to pass by reference(as opposed to pointer) because a reference cannot have a null value like a pointer can. Besides being safer, the syntax is nicer since to don't need to take an object's address when passing it in. I hope this helps.

-Scott

Share this post


Link to post
Share on other sites
For input parameters (those that are not modified), you should use:
void func(const MyType& param);

For output/inout parameters, you should use:
void func(MyType* param);



Using a reference for output parameters my lead to programming errors. Consider a function declaration like this:
void func(MyType& param);

The you would call it like this:
MyType myValue;
func(myValue);

It's not obvious from the call itself, that you call by reference instead calling by value (*). But if you used a pointer, you had to write this:
MyType myValue;
func(&myValue);

Here you can clearly see (when calling the function), that the function is able to modify the parameter. This avoids much confusion when reading your source code, especially if you can't remember the signatures of 1000 functions at once.


(*)
Pass by value:
void func(MyType myValue);

Share this post


Link to post
Share on other sites
I would use references as much as you possibly can - if you're worried about altering values then use consts.

References look so much cleaner - no unneccasary dereferencing etc, and no worrying about NULL or uninitialised pointers.

There are situations however when pointers the only alternative - but I would try and keep these to a minimum to save debugging time.

Share this post


Link to post
Share on other sites
Quote:
Original post by nmi
Using a reference for output parameters my lead to programming errors. Consider a function declaration like this:
void func(MyType& param);

The you would call it like this:
MyType myValue;
func(myValue);

It's not obvious from the call itself, that you call by reference instead calling by value (*). But if you used a pointer, you had to write this:
MyType myValue;
func(&myValue);

Here you can clearly see (when calling the function), that the function is able to modify the parameter. This avoids much confusion when reading your source code, especially if you can't remember the signatures of 1000 functions at once.

Which parameters will and will not be modified should be clear from the function name. Using pointers adds the possibility of being passed invalid memory, which is considerably worse than forgettining which arguments might be modified. References don't suffer from this problem.

CM

Share this post


Link to post
Share on other sites
Use references as much as you can but don't forget that sometimes pointers are the best or easiest way to go, for example its easier to make an array of pointers than it is to make an array of references and its sometimes nice to be able to pass NULL (although I prefer to use 0 rather than NULL)

'highly' portable code might use pointers because C doesn't have references (pointers are the ONLY alternative to pass-by-value)

But don't pass everything by reference eg passing an integer by value is better than by reference, only pass complex types by reference (unless you plan to modify it within the function and DON'T want it to affect the object outside the function)

Share this post


Link to post
Share on other sites
Great advice, thanks guys. It seems the concensus is to use pass by reference then. It that case, my final question is... using hungarian notation, which is the right format?

void Test(Tile &InTile)
{
}

or

void Test(Tile &pInTile)
{
}

Should it still be declared within the function as a pointer?

Thanks again all!

Share this post


Link to post
Share on other sites
Quote:
Original post by Raeldor
Great advice, thanks guys. It seems the concensus is to use pass by reference then. It that case, my final question is... using hungarian notation, which is the right format?

void Test(Tile &InTile)
{
}

or

void Test(Tile &pInTile)
{
}

Should it still be declared within the function as a pointer?

Thanks again all!

The right format is probably not to use hungarian notation at all. Either way, there's no need to add the p. That's intended to remind you to dereference it and what not, none of which is necessary with references. For all practical purposes, they're just like any other variable.

CM

Share this post


Link to post
Share on other sites
I consider hungarian notation the tool of the devil who will laugh at you if you ever try to change the type of a variable...

That said, definately not p, since it's not a pointer.
Maybe r or ref, or prehaps none extra (e.g. Tile & ____ would be the same as Tile ____, since they act exactly the same).

Share this post


Link to post
Share on other sites
Quote:
Original post by MaulingMonkey
I consider hungarian notation the tool of the devil who will laugh at you if you ever try to change the type of a variable...

That said, definately not p, since it's not a pointer.
Maybe r or ref, or prehaps none extra (e.g. Tile & ____ would be the same as Tile ____, since they act exactly the same).


Is hungarian notation bad? I was lead to believe it was the best form to use? What are the alternatives?

Share this post


Link to post
Share on other sites
*edit: I assume you figured this out on your own. As to Hungarian Notation, it just clutters your variable names, without adding anything useful. You should be able to tell from the name of the variable if its an int or string or whatever, so adding i, sz, etc is a waste of time. Not only that, but if you ever need to change the behavior [perhaps you replace a char* with a std::string, or an int with a long], all your variable names become wrong.

Quote:
Original post by Raeldor
I am a little confused... you say it's not a pointer? Doesn't being passed by reference mean that the variable name is now a pointer and needs the '->' operator to access members?

No. A reference is *like* a pointer, in that it references memory allocated elsewhere. But you don't treat it like a pointer. For your purposes, its just another variable and you treat it as such.


void func(int& i)
{
i = 5;
//*i = 5; <--compiler error
}

int main()
{
int i = 0;
cout << i << endl; //prints '0'
func(i);
cout << i << endl; //prints '5'
return 0;
}

Share this post


Link to post
Share on other sites
My advice would be not to use hungarian notation. I used it years ago, but learned the error of my ways when I started posting here! As said, what if you have a variable that's an integer, and later decide to change it to a float? Either you have to change its type, and have the naming convention be innacurate, or you've got to find and replace all code that references it. My understanding is that hungarian notation is a throwback to the days of C, when it was much easier to use a variable of the wrong type, without the compiler complaining.

Then there's the issue of classes, and structs. What's the correct notation to use for MyClass?

As for the naming convention to use. I wouldn't say that there's a "best convention" to use, as long as you have a good convention, and stick to it. I'd argue in favour of using descriptive variable names where possibe.

Personally, I use the following convention.

variableName
ClassName
FunctionName
EEnumName

With regards to not knowing the type of a variable by its name, descriptive variable names can help with that. Besides which, any IDE worth its salt should allow you to see the type of a variable. Besides which, how often is it, that you know absolutely nothing about the code you're maintaining, when you're messing with it. Surely you shouldn't be changing code if you have no idea what it does?

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Original post by Conner McCloud
*edit: I assume you figured this out on your own. As to Hungarian Notation, it just clutters your variable names, without adding anything useful. You should be able to tell from the name of the variable if its an int or string or whatever, so adding i, sz, etc is a waste of time. Not only that, but if you ever need to change the behavior [perhaps you replace a char* with a std::string, or an int with a long], all your variable names become wrong.

Quote:
Original post by Raeldor
I am a little confused... you say it's not a pointer? Doesn't being passed by reference mean that the variable name is now a pointer and needs the '->' operator to access members?

No. A reference is *like* a pointer, in that it references memory allocated elsewhere. But you don't treat it like a pointer. For your purposes, its just another variable and you treat it as such.


void func(int& i)
{
i = 5;
//*i = 5; <--compiler error
}

int main()
{
int i = 0;
cout << i << endl; //prints '0'
func(i);
cout << i << endl; //prints '5'
return 0;
}


I see. So if I was passing in a reference to an instance of my class from a function within that class, I would have to use...

AnotherClass(*this);

If that parameter was pass by reference?

Share this post


Link to post
Share on other sites
I think most people agree Hungarian notation is old-fashioned. Most current IDE show tooltips with the type of the variable anyway.

What I have found useful is using prefixes for member variables, static variables and global ones, like this:
g_myGlobalVariable
m_aMemberVariable
sm_staticMember

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
I see. So if I was passing in a reference to an instance of my class from a function within that class, I would have to use...

AnotherClass(*this);

If that parameter was pass by reference?

Yep.

CM

Share this post


Link to post
Share on other sites
The best alternative to hungarian notation is simply giving your variables good names :-). As an example, I will show a case where hungarian notation could be innapropriate:

struct PlayerInfo {
unsigned int uPlayerID;
};

Now, we have a fun and wonderful time throwing around uPlayerID in our little MMORPG/Quake3/InsertOtherBigProjectHere.

So, we've been using it as our player names too (okay, so that was probably naughty, but it works). We decide that really, PlayerID should be a string, so we can let CluelessNewbie01 be distinguished from 1337|=|4><><00rzJ4/\/\3s|34|_||). To prevent people from getting horribly confused when they find out that "uPlayerID" isn't a unsigned integer at all... we now have to go and find every instance of uPlayerID and replace it with strPlayerID, or whatever else we've renamed PlayerID to.

As for references and them not being pointers - internally, they're pointers. However, they don't act like pointers, in that you don't dereference them to use them. Anyone who sees the p prefix will automatically assume that they're dealing with "pointer pointers" (that is, with a * in the variable decleration) and not "reference pointers" (that is, with a & in the variable decleration).

Share this post


Link to post
Share on other sites
Quote:
Original post by Raeldor
Is hungarian notation bad? I was lead to believe it was the best form to use? What are the alternatives?


Put it this way microsoft have virtual banned its use and recommend others to follow suit, particularly in C#. Hungarian notation in its full form is not needed any more on modern IDEs, i've always thought it was ugly from first moment i came across it.

Along while ago i used camel notation for abit but then it just got ugly mixing two styles that is the one used by C++ standard library and camel notation so i changed all my C++ code to notation simillar to that used in C++ standard library as does the boost library.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this