Reference parameters vs. pointers

Started by
15 comments, last by Ecco2000 23 years, 6 months ago
Whoa! I just tried to compile the int k(45); in my old DOS C++ compiler, and it gave me an error. When I compiled it in my Win32 compiler, it worked, but I still don''t know for a fact if it initializes the variable, I''ll just take your word for it :D.
Advertisement
Make a console app and then do this:

using namespace std;
int main()
{
int k(45);
cout << k << endl;
}

References can''t do this: int &r = new int(99); so instead use int *r = new int(99);

Also, the () syntax is the only way to initialize the base class constructors if they don''t have default values like so:

class B
{
protected:
int ii, jj;
public:
B(int i, int j){ii = i; jj = j;}
void show(){cout << ii << jj << endl;}
};

class C : public B
{
string str;
public:
C(int i, int j, string s):B(i, j), str(s){}
void show(){cout << ii << jj << ''\n'' << str.c_str() << endl;}
};

int main(int argc, char* argv[])
{
B objB(1, 2);
C objC(3, 4, "\"hello\"");

objB.show();
objC.show();

return 0;
}

Here the objB initializes its parts normally, while objC needs to also initialize its base class parts because there is no default constructor in the base class. If you make either a default constructor or copy constructor in a class then the compiler doesn''t create the other types of constructors for you because it figures out that you know what you''re doing. Ok the line:
C(int i, int j, string s):B(i, j), str(s){}

means that when you create an objC you pass it the 3 and 4 which go further up the hiarchy and the values will be copied to ii and jj variables of the objB through its constructor.

Then the "hello" string will be copied and assigned to objC str variable. All this using that funny int k(45) syntax seen before. Finally the show() of objC will get invoked and out comes the values 3,4 of the ii,jj variables that were inherited from objB. I agree the int k = 45 is visually better.
A common rule of thumb regarding references and pointers as arguments to functions ( taken more or less from Effective C++ by Meyer )

If you are going to modify an argument, pass it in by pointer as then it looks like a variable that can be changed.

Otherwise, pass by const-reference, for arguments that will not change upon return.

Now the STL often defies this rule of thumb but it is generally good to observe it.

The reason is as follows - consider the following code snippets.
Now consider that the caller of the function does not see the declaration of the function - typically they won''t go look at it in another header file:

    // In a header fileclass MyType;void FuncThatMayModifyAReferenceParameter(MyType& ref);// in a source file.MyType theThing;// do some stuff with ''theThing''FuncThatMayModifyAReferenceParameter( theThing );Assert( ...THING HAS NOT CHANGED... );    


Suddenly it becomes not so obvious why the assert is failing.
Because passing something in by reference _looks_ like pass by value.

Since that is the case, it is better to make it behave like pass by value and pass in a const reference if the value will not be modified or a pointer if it will.




Good programmers should be able to handle either references or pointers as parameters. For complicated data types (i.e. anything bigger than an unsigned long), you should pass by reference/pointer anyway simple to avoid copying.

It''s bad to have the mindset "if I have to pass an object by its address, then the function must be able to change it". Instead, get into the mindset "If the function takes a const parameter, it can''t change it. If it doesn''t, it will change it." Much better, and more accurate.

Const is your friend (I''ve said it once, I''ll say it again).

There''s also a subtle difference between using references and pointers. References--without any complicated trickery--don''t usually have a value of NULL. Pointers, however, can. Therefore the following function uses pointers effectively but references would be inappropriate:
void getProgramStats (int* frame_rate, int* time_elapsed, int* num_turns){  if (frame_rate) *frame_rate = m_frame_rate;  if (time_elapsed) *time_elapsed = m_time_elapsed;  if (num_turns) *num_turns = m_num_turns;} 


I find myself favoring references over pointers, because it avoids nesting pointers-to-pointers-to-pointers. However, if an input or output variable is optional, I find it useful to allow the user to pass a NULL pointer and to handle it differently within the function.
Stoffel:

The problem with just ''knowing'' a function takes a non-const parameter it can change is it how it looks:

e.g.

    // defined somewhere...class MyType;MyType x;DoSomething( x );    


We can certainly tell that it is not a pass by pointer.
However, can you tell from the above snippet if it is a pass by value or a pass by reference?

On just inspecting this code snippet you can''t tell if it is pass by value or reference.
However, if you followed the general rule that you would pass only by const-reference then you can feel pretty certain that DoSomething() is not changing the x object.

This can help make debugging easier because you don''t always have to lookup a function to find out what is going on.
Not that you shouldn''t look it up, but it is often faster to not worry about this. Especially when working in a code base that is millions of lines of code.

But like I said, it is a rule of thumb. And there are instances where I''ve preferred non-const reference because I don''t want to worry about checking for null parameters.
quote:
This can help make debugging easier because you don''t always have to lookup a function to find out what is going on.
Not that you shouldn''t look it up, but it is often faster to not worry about this. Especially when working in a code base that is millions of lines of code.

I work with a code base of millions of lines of code. Not only do we have to rely on looking at the prototypes of each function for the arguments; we also have to look at whether member functions change their objects (i.e. are const member functions).

If you''re using a function, you must know its prototype. Relying on anything else as a shortcut is a surefire way to shoot yourself in the foot.

BTW, MSVC 6.0 goes a long way toward making this easier with pop-up prototypes that show the prototype as a hint while you''re entering its parameters. I''m sure there are plug-ins for most compilers to do this nowadays.
WhatEver:

Your DOS compiler is probably old - the construction syntax was "new" a couple of years ago, IIRC. Like C, C++ is a work in progress, and is constantly changing.


back to general discussion:

Hmm...this discussion is getting off-topic...


References:


  • can only be set once

  • have the same syntax as the object''s type

  • force errors upon a user before the function is called, if he/she tries to dereference NULL pointers




Pointers:


  • can be set many times

  • have a special pointer syntax, great for low-level stuff like mixing/streaming audio, etc.

  • can be NULL without errors

  • make the user use a special syntax for passing stack-based variables or references




It goes without saying that both have their pros and cons, so which one you use depends on the current programming problem. Each class that I write imposes some sort of rules on the user of the class (for both speed and minimizing bugs), so the use of references is just fine. Classes should be rather small; large, complex classes indicates poor design. Rules should be simple and obvious. By rules, I mean:


class my_directdrawsurface_wrapper_class
{
public:
void lock();
void unlock();
byte* const get_memory();
};

// RULE: The memory returned by get_memory() is only valid
// while the object is locked.



Imposing rules on the parameters and return values helps to make the code smaller, clearer, and more efficient. We can''t be locking/unlocking the surface every time we wish to change a pixel. It goes without saying that there is a proper limit to what rules you can impose and what you cannot...

My personal experience is that references can provide cleaner syntax with small inline functions, like some overloaded operators, constructors, etc. Also, IMO, Java uses references to what are really heap-based variables (although you can''t tell) and there is no stack in Java, per se, so C++ references are really just a good idea taken from Java. Or were references added to the language before Java became popular?


- null_pointer
Sabre Multimedia

This topic is closed to new replies.

Advertisement