Archived

This topic is now archived and is closed to further replies.

Ecco2000

Reference parameters vs. pointers

Recommended Posts

I''ve been reading "Practical C++ Programming" and they discuss reference variables as a way to call byref, are reference variables the same as pointers. Reference variable void FunctionThing(float ¶m) { } Pointer void FunctionThing(float* param) { }

Share this post


Link to post
Share on other sites
i haven''t actually disassembled it myself, but i''ve heard many times "yes"

the idea is to make function(const CComplexObject&)
so that you just call the function as normal, or the person using your code calls it as usual, and the information is efficiently pasted from your code to thiers - and they can''t twiddle your data. If you want to change something in the object, then it makes more sense (to me) to pass a pointer to it, which implies that you''re going to change it in some manor.

Share this post


Link to post
Share on other sites
I would like to add to this by saying that once a reference is refering to a variable you can not make that reference refer to another variable but a pointer can. Example:

// reference
int k(45);
int h;
int &rk = k;
rk = 77;
rk = h; // doesn''t work

// pointer
int *p = &k;
*k = 88;
int g(4);
p = &g;
*p = 44;

References automatically dereference the pointer for you so it looks like you''re manipulating the object itself and not through a pointer. I.e. obj.m = 5 rather than obj->m = 5 (i.e. (*obj).m = 5) where obj is a reference to an object.

References come in handy when you use overloaded operators and try to do something like this:

objD = objA + objB + objC;
// i.e.
objD = objA.operator+(objB.operator+(objC));
// in each case a *this is returned from within CObj& operator+(const CObj &rhs) function. You can also do this:
objA + objB = 10;
// remember the assignment operator i.e. operator=() is used here not the regular = operator that is used with built in types because objB is an object and its assignment operator is called instead. You define whatever the operator=() should do but basically it should check for self assignment i.e. is objB == objB when you have heap allocated objects in objB then if self assignment is true return *this otherwise delete heap allocated objects in objB, allocate new heap blocks with new and finally assign the right hand side i.e. 10 into the heap of objB. Once that''s done then invoke operator+() of objA and add data members of objB to objA.

It''s late and I hope this is correct if there are mistakes I apologize. I''m learning c++ too you know

my homepage
E-Mail: BlueOrbSoftware@mailcity.com

Share this post


Link to post
Share on other sites
My take on passing by referance. It's better to make it a pointer because there's a visual referance to what is happening if you need to debug your program.

In case you don't know exactly what a referance is, it's basicly making a copy of your variables value and address, so when you use it in your function, it's as if the variable was global to begin with. Referances can only be created once, because it's only a copy of another variable.

If you don't use referances, it makes it a whole lot easier to read your code and other peopls code plus it's easier to visualy catch a pass by pointer method.

That's my 2 cents.

Edited by - WhatEver on October 7, 2000 10:54:56 AM

Share this post


Link to post
Share on other sites
I thought I''d bring to your attention an explanation to your code...

int k(45); // do parenthisis work to init arrays? If not, what is this?
int h;
int &rk = k;
rk = 77; /// this works because it thinks it''s an address
rk = h; // doesn''t work //because it''s not an address

//this will work
*rk=h

Share this post


Link to post
Share on other sites
WhatEver:

int k(45); // do parenthisis work to init arrays? If not, what is this?

this is simply the c++ way to initialize a variable

int k = 45; // C way
int k(45); // C++ way

at least i think so i''m digging deep into my memory for this one. I prefer the c way because it''s more clear.

Share this post


Link to post
Share on other sites
I''m pretty sure Whatever is not correct about how references work. They create a ''hidden'' pointer, they don''t copy any variables'' values... if that were that case, you may as well just pass by value. It would actually take longer to pass by ref then, because you''d have to copy it again, on return, back to its orginal location.

Share this post


Link to post
Share on other sites
The most useful feature of reference values as opposed to pointers is in the return values of functions.

If you define a function with a reference return value eg (in a class):

class MyClass {
public:
inline int &GetX() { return m_x; };
private:
int m_x;
};

then you can use the expression pObject->GetX() on the LHS of an expression, e.g.:

MyClass *pObject;
...
pObject->GetX() += 30;

Dave

Edited by - Heraldin on October 7, 2000 8:44:12 PM

Share this post


Link to post
Share on other sites
The syntax when using references is a lot more simplified, if a little less flexible. I like using references in classes to return objects from member functions or operator overloads:

string& operator+(string& STRING) // concatenates a ''string''

Doing things this way, you never need to use a pointer in siuations like these. References will do just fine and I use them as much as possible because they are simpler than pointers.

I''m not sure if a pointer is the same as a reference when it''s borken down to assembly but it is probably very similar. Which makes references a better choice whenever you can use them instead of pointers.




-=[ Lucas ]=-

Share this post


Link to post
Share on other sites
I explained the referance wrong. The only way I could think to explain it was by using the word copy. The word alias would have best described what I meant.

Here it is straight out of my pointers book:

quote:
The crucial point to understand here is that a referance is not a copy of the variable in which it refers. It is the same variable, just under a differant name.


That makes sence about the "int k(45);". I''ll try it out later tonight to make sure, because that might come in handy, but might avoid it because not everyone would recognize that syntax.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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 file

class 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.




Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites