Tuesday, August 18, 2009

c++: reference tips

---c++ compiler imp reference s pointers: take address and same memory
13
I know references are syntactic sugar, so easier code to read and write :)

But what are the differences?
Summary from answers and links below:
1.A pointer can be re-assigned any number of times while a reference can not be reassigned after initialization.
2.A pointer can point to NULL while reference can never point to NULL
3.You can't take the address of a reference like you can with pointers
4.There's no "reference arithmetics" (but you can take the address of an object pointed by a reference and do pointer arithmetics on it as in &obj + 5).
To clarify a misconception:
The C++ standard is very careful to avoid dictating how a compiler must implement references, but every C++ compiler implements references as pointers. That is, a declaration such as:
int &ri = i;
allocates the same amount of storage as a pointer, and places the address of i into that storage.

So pointer and reference occupies same amount of memory
As a general rule,
•Use references in function parameters and return types to define attractive interfaces.
•Use pointers to implement algorithms and data structures.





------
do not return reference of temporary (stack) variable/objs.
do not return reference of vector element since it can be resize, you
are loing to lose that original reference.
ref must be initialized: int& x = p;


int& getInt(void){ int i; return i;}That is all sorts of evil. The stack-allocated i will go away and you are referring to nothing. This is semi-evil:

int& getInt(void){ int *i = new int; return *i;}Because now the client has to eventually do the strange:

int& myInt = getInt(); // note the &.int badInt = getInt(); // the & will be easy to miss (source of problems).delete &myInt; // must delete.delete &badInt; // won't work. badInt was a copy of the allocated int, which // is now lost foreverI think the best way to do something like that is just:

int *getInt(void){ return new int;}And now the client stores a pointer:

int *myInt = getInt(); // has to be a pointerint& weirdInt = *getInt(); // but this works too if you really want.delete myInt; // being a pointer, this is easy to do.delete &weirdInt; // works.Now for members of classes, & is powerful, such as operator chaining (cout's <<), or operator[].







Pro's and Con's of returing references
I've recently began working on a moderate size C++ application. The previous developer loved the benefits of returning const&.

For those who don't know returning const& can avoid an extraneous copy. Example:


class Foo { Foo(std::string s) { m_name = s; } std::string m_name;public: std::string const& name() { return m_name; }}

The above code avoids the copy from m_name to the function return value that would have resulted with the following client code:


Foo f("bar"); std::string name = f.name();


After working in this code base for a while now I believe that returning references are evil and should be treated just like returning a pointer, which is avoid it.

For example the problem that arose that took a week to debug was the following:


class Foo { std::vector< Bar > m_vec; public: void insert(Bar& b) { m_vec.push_back(b); } Bar const& getById(int id) { return m_vec[id]; } }

The problem in this example is clients are calling and getting references that are stored in the vector. Now what happens after clients insert a bunch of new elements? The vector needs to resize internally and guess what happens to all those references? That's right there invalid. This caused a very hard to find bug that was simply fixed by removing the &.

The conclusion I've arrived at is that returning a & is a pre-mature optimization. If your profiling shows you your code is very slow at this point than maybe consider it. But you should also be aware that most modern compilers optimize away this extra copy anyways.

Here is a contrived example that shows problems you can get into by blindly returning references. My advice avoid them until your profiler tells you that you have a problem.

#include #include #include using namespace std;class foo { std::string m_string; public: std::string const getName(){ static int count = 0; count++; std::stringstream ss; ss << count ; m_string = ss.str(); return m_string; }};foo f;void fun(std::string const&s) { f.getName(); std::cout << s;}int main() { std::cout << " print version " << std::endl; fun(f.getName());}

Hope that Helps
Posted by developer-resource at 10:27 PM
Labels: c++
3 comments:
r.o.e said...
why not mark the accessor as const if you intended it to be read-only, like this
Bar const& getById(int id) const
{ return m_vec[id]; }


---------
more
2) A pointer has its own memory address and size on the stack (4 bytes on x86), whereas a reference shares the same memory address but also takes up some space on the stack. Since a reference has the same address as the original variable itself, it is safe to think of a reference as another name for the same variable. Note: What a pointer points to can be on the stack or heap. Ditto a reference. My claim in this statement is not that a pointer must point to the stack. A pointer is just a variable that holds a memory address. This variable is on the stack. Since a reference has its own space on the stack, and since the address is the same as the variable it references. More on stack vs heap. This implies that there is real a address of a reference that the compiler will not tell you.

int x = 0;int &r = x;int *p = &x;int *p2 = &r;assert(p == p2);3) you can have pointers to pointers to pointers offering extra levels of indirection. Whereas references only offer 1 level of indirection.

int x = 0;int y = 0;int *p = &x;int *q = &y;int **pp = &p;pp = &q;//*pp = q**pp = 4;assert(y == 4);assert(x == 0);4) Pointer can be assigned NULL directly, whereas reference cannot. If you try hard enough, and you know how, you can make the address of a reference NULL. Likewise, if you try hard enough you can have a reference to a pointer, and then that reference can contain NULL.

int *p = NULL;int &r = NULL; <--- compiling error5) Pointers can iterate over an array, you can use ++ to go to the next item that a pointer is pointing to, and + 4 to go to the 5th element. This is no matter what size the object is that the pointer pints to.

6) A pointer needs to be dereferenced with * to access the memory location it points to, whereas a reference can be used directly. A pointer to a class/struct uses -> to access it's members whereas a reference uses a .

7) A pointer is a variable that holds a memory address. Regardless of how a reference is implemented, a reference has the same memory address the item it references.

8) References cannot be stuffed into an array, whereas pointers can be (Mentioned by user @litb)
link|flag edited Feb 27 at 21:13
Brian R. Bondy
community wiki

14 revisions, 2 users
Brian R. Bondy 97%

Reference can be null. In some cases, you pass refrence params in as this: function(*ptr); If ptr is NULL, so will be the reference. – Arkadiy Sep 11 at 21:01
...but dereferencing NULL is undefined. For example, you can't test if a reference is NULL (e.g., &ref == NULL). – Pat Notz Sep 11 at 22:07
IN VC++ *ptr will crash, pretty sure in gc++ it will segfault too – Brian R. Bondy Sep 11 at 23:37
I think point 2 is incorrect. See this example program: pastebin.com/f5252f8a8 This implies that on gcc on my machine, the reference is implemented with a pointer. Output: Size of class: 8 Value of ref before hack: 1 Value of ref after hack: 2 Value of ref after hack, with y changed: 3 – Nick Sep 12 at 14:21
Number 2 is not true. A references is not simply "another name for the same variable." References may be passed to functions, stored in classes, etc. in a manner very similar to pointers. They exist independently from the variables they point to. – Derek Park Sep 12 at 23:37




Contrary to popular opinion, it is possible to have a reference that is NULL.

int * p = NULL;int & r = *p;r = 1; // crash! (if you're lucky)Granted, it is much harder to do with a reference - but if you manage it, you'll tear your hair out trying to find it.

Edit: a few clarifications.

Technically, this is an invalid reference, not a null reference. C++ doesn't support null references as a concept, as you might find in other languages. There are other kinds of invalid references as well.

The actual error is in the dereferencing of the NULL pointer, prior to the assignment to a reference. But I'm not aware of any compilers that will generate any errors on that condition - the error propagates to a point further along in the code. That's what makes this problem so insidious. Most of the time, if you dereference a NULL pointer, you crash right at that spot and it doesn't take much debugging to figure it out.

My example above is short and contrived. Here's a more real-world example.

class MyClass{ ... virtual void DoSomething(int,int,int,int,int);};void Foo(const MyClass & bar){ ... bar.DoSomething(a,Long,list,of,parameters); // crash occurs here - obvious why?}MyClass * GetInstance(){ if (somecondition) return NULL; ...}MyClass * p = GetInstance();Foo(*p);link|flag edited Feb 27 at 22:44

answered Sep 11 at 21:06
Mark Ransom
12.6k●7●27

As a nitpick, I'd say that the reference isn't actually null - it references bad memory. Although it is a valid point that just because it's a reference, doesn't mean that it refers to something that's valid – Nick Sep 12 at 13:42
1
The code in question contains undefined behavior. Technically, you cannot do anything with a null pointer except set it, and compare it. Once your program invokes undefined behavior, it can do anything, including appearing to work correctly until you are giving a demo to the big boss. – KeithB Sep 12 at 16:00
I didn't test it but I think the code above just crash at the second line while dereferencing the NULL pointer. – Vincent Robert Sep 19 at 12:00
@Vincent: In fact, no, sometimes, the second line is silently invoked (remember compilers can implement references as pointers, so...)... So the crash happens when you us the reference. – paercebal Oct 10 at 20:39
Anyway, the coder dereferenced a pointer without testing it. This is the source of the error. At that point, or sometimes after, the program will crash. This can happen everytime a pointer is converted to a reference. It means that you can reduce the risk by removing as much pointers as possible... – paercebal Oct 10 at 20:40

show 2 more comments

3

If you want to be really pedantic, there is one thing you can do with a reference that you can't do with a pointer: extend the lifetime of a temporary object. In c++ if you bind a const reference to a temporary object, the lifetime of that object becomes the lifetime of the reference.

std::string s1 = "123";std::string s2 = "456";std::string s3_copy = s1 + s2;const std::string& s3_reference = s1 + s2;In this example s3_copy copies the temporary object that is a result of the concatenation. Whereas s3_reference in essences becomes the temporary object, it's really a reference to a temporary object that now has the same lifetime as the reference.

If you try this without the const it should fail to compile. You cannot bind a non-const reference to a temporary object, nor can you take its address for that matter.
link|flag answered Sep 11 at 21:43
Matt Price
2,842●6●17



2

A reference on the stack doesn't take up any space at all. Or rather, it doesn't matter how much space it takes up since you can't actually see any side effect of whatever space it would take up.

On the other hand, one major difference between references and pointers is that temporaries assigned to const references live until the const reference goes out of scope.

For example:

class scope_test{public: ~scope_test() { printf("scope_test done!\n"); }};...{ const scope_test &test= scope_test(); printf("in scope\n");}will print:

in scopescope_test done!This is the language mechanism that allows ScopeGuard to work.

MSN
link|flag answered Sep 12 at 23:27
MSN
5,013●6●17



2

A very simple answer is that the only really significant things that a reference has in common with a pointer are:

1.A reference can be made to refer to a pre-existing memory location, which a normal variable cannot.
2.A reference can be of a type that is a parent or child class of the original variable's type.
Otherwise, a reference basically acts like the original variable. It is effectively an alias to the original variable.
link|flag edited Sep 16 at 13:05

answered Sep 11 at 20:30
Turbulent Intellect
1,841●3●18

1
"It is effectively an alias to the original variable." - I wouldn't go so far - in most cases, using references will have the same performance hit as using pointers: the indirection still has to happen! – Christoph Feb 27 at 21:32
1
Certainly, but I'd question how often this performance difference is really relevant except in the most performance-intensive applications or efficiency-sensitive platforms. Unless your code is already highly tuned, pointers/references aren't likely to be your bottleneck. – Turbulent Intellect Mar 2 at 15:19


1

Apart from syntactic sugar, a reference is a const pointer (not pointer to const thing, a const pointer). You must establish what it refers to when you declare the reference variable, an you cannot change it later.
link|flag edited Sep 14 at 14:28
Prakash
answered Sep 11 at 20:07
Arkadiy
4,320●6●20

I'm not sure that calling a reference a "const pointer" is helpful. The only way in which a reference is like a pointer is that it can be made to refer to some pre-existing memory location. A reference shares no other characteristics of a pointer. – Turbulent Intellect Sep 11 at 20:17
@Turbulent Intellect: a C++ reference can be thought of as a constant pointer with automatic indirection - see my answer... – Christoph Feb 27 at 21:28


0

@axs6791 I'm not sure what you mean exactly. References can be assigned to a derived class:

class A{};class B : public A{};void foo(){ B b; A& a = b;}What case can you do with pointers that wouldn't work with references?
link|flag answered Sep 11 at 20:24
Rob Walker
13.5k●15●40



0

You forgot the most important part

member-access with pointers uses ->
member-access with references uses .

foo.bar is clearly superior to foo->bar in the same way that vi is clearly superior to emacs :-)
link|flag answered Sep 11 at 22:10
Orion Edwards
17.1k●1●23●63

here we go..... :P – matpalm Oct 10 at 9:09


0

@Brian: References can take up memory, just as pointers do.

For example, if you have a member variable of a class which is a reference, then every instance of the class will have memory allocated for that reference.

In some cases, a compiler can optimize away the need to allocate memory for a reference; and there's no nice way to get the address of a reference in your code (&my_reference just returns the address of the referred-to object). In those aspects I agree that references act as if they don't take up memory - but they can.
link|flag answered Sep 12 at 12:00
Tyler
3,045●1●3●16



0

@Orion Edwards

member-access with pointers uses ->

member-access with references uses .

This is not 100% true. You can have a reference to a pointer. In this case you would access members of de-referenced pointer using ->

struct Node { Node *next; };Node *first;// p is a reference to a pointervoid foo(Node*&p) { p->next = first;}Node *bar = new Node;foo(bar);--

OP: Are you familiar with the concepts of rvalues and lvalues?
link|flag answered Sep 12 at 12:57
van_houtte
61●1●3



0

I use references unless I need either of these:

•Null Pointers can be used as a sentinel value, often a cheap way to avoid function overloading or use of a bool.

•You can do arithmetic on a pointer. For example, p += offset;
link|flag answered Sep 12 at 13:41
Aardvark
2,150●1●6●17



0

Another interesting use of references is to supply a default argument of a user-defined type:

class UDT{public: UDT() : val_d(33) {}; UDT(int val) : val_d(val) {}; virtual ~UDT() {};private: int val_d;};class UDT_Derived : public UDT{public: UDT_Derived() : UDT() {}; virtual ~UDT_Derived() {};};class Behavior{public: Behavior( const UDT &udt = UDT() ) {};};int main(){ Behavior b; // take default UDT u(88); Behavior c(u); UDT_Derived ud; Behavior d(ud); return 1;}The default flavor uses the 'bind const reference to a temporary' aspect of references.
link|flag answered Sep 12 at 17:59
Don Wakefield
1,435●2●13



0

Actually, a reference is not really like a pointer.

A compiler keeps "references" to variables, associating a name with a memory address, that's its job to translate any variable name to a memory address when compiling.

When you create a reference, you only tell the compiler that you assign another name to the pointer variable, that's why references cannot "point to null", because a variable cannot be, and not be.

Pointers are variables, they contain the address of some other variable, or can be null. The important thing is that a pointer has a value, while a reference only has a variable that it is referencing.

Now some explanation of real code:

int a = 0;int& b = a;Here you are not creating another variable that points to a, you are just adding another name to the memory content holding the value of a. This memory now has two name, a and b, and can be addressed using either name.

void increment(int& n){ n = n + 1;}int a;increment(a);When calling a function, the compiler usually generates memory spaces for the arguments to be copied to. The function signature defines the spaces that should be created and gives the name that should be used for these spaces. Declaring a parameter as a reference just tells the compiler to use the input variable memory space instead of allocating a new memory space during the method call. It may seem strange to say that your function will be directly manipulating a variable declared in the calling scope but remember that when executing a compiled code, there is no more scope, there is just plain flat memory and your function code could manipulate any variables.

Now there may be some cases where your compiler may not be able to know the reference when compiling, like when using an extern variable. So a reference may or may not be implemented as a pointer in the underlying code. But in the examples I gave you, it will most likely not be implemented with a pointer.
link|flag answered Sep 19 at 12:23
Vincent Robert
4,532●7●19

A reference is a reference to l-value, not necessarily to a variable. Because of that, it's much closer to a pointer than to a real alias (a compile-time construct). Examples of expressions that can be referenced are *p or even *p++ – Arkadiy Mar 2 at 16:27
Right, I was just pointing the fact that a reference may not always push a new variable on the stack the way a new pointer will. – Vincent Robert Mar 3 at 20:36


0

What's a C++ reference (for C programmers):

A reference can be thought of as a constant pointer (not a pointer to a constant value!) with automatic indirection - ie the compiler will apply the * operator for you.

All references must be initialized with a non-null value or compilation will fail. It's neither possible to get the address of a reference - the address operator will return the address of the referenced value instead - nor is it possible to do arithmetics on references.

C programmers might dislike C++ references, as it will no longer be obvious when indirection happens or if an argument gets passed by value or by pointer.

C++ programmers might dislike using pointers, as they are considered 'unsafe' - although references aren't really any safer than constant pointers - and lack the convenience of automatic indirection.

Consider the following statement from the 'C++ FAQ Lite':

"Even though a reference is often implemented using an address in the underlying assembly language, please do not think of a reference as a funny looking pointer to an object. A reference is the object. It is not a pointer to the object, nor a copy of the object. It is the object."

That's how some people want you to think about references. But if a reference really were the object, how could there be dangling references? In unmanaged languages, it's impossible for references to be any 'safer' than pointers - there just is no way to alias values across scope boundaries!

Coming from a C background, C++ references may look like a somewhat braindead concept, but one should still use them instead of pointers if possible - when in Rome...

No comments:

Post a Comment