Friday, May 4, 2012

reference semantics in c++

Value Semantics or Reference Semantics

All containers create internal copies of their elements and return copies of those elements. This means that container elements are equal but not identical to the objects you put into the container. If you modify objects as elements of the container, you modify a copy, not the original object.

Copying values means that the STL containers provide value semantics. They contain the values of the objects you insert rather than the objects themselves. In practice, however, you also need reference semantics. This means that the containers contain references to the objects that are their elements.

The approach of the STL, only to support value semantics, has strengths and weaknesses. 

Its strengths are:
  • Copying elements is simple.
  • References are error prone. You must ensure that references don't refer to objects that no longer exist. You also have to manage circular references, which might occur.

Its weaknesses are:
  • Copying elements might result in bad performance or may not even be possible.
  • Managing the same object in several containers at the same time is not possible.

In practice you need both approaches; you need copies that are independent of the original data (value semantics) and copies that still refer to the original data and get modified accordingly (reference semnatics). Unfortunately, there is no support for reference semantics in the C++ standard library. However, you can implement reference semantics in terms of value semantics.

The obvious approach to implementing reference semantics is to use pointers as elements. However, ordinary pointers have the usual problems. For example, objects to which they refer may no longer exist, and comparisons may not work as desired because pointers instead of the objects are compared. Thus, you should be very careful when you use ordinary pointers as container elements.

Note: C programmers might recognize the use of pointers to get reference semantics. In C, function arguments are able to get passed only by value, so you need pointers to enable a call-by-reference.

A better approach is to use a kind of smart pointer — objects that have a pointer-like interface but that do some additional checking or processing internally. The important question here is, how smart do they have to be? The C++ standard library does provide a smart pointer class that might look like it would be useful here: auto_ptr. However, you can't use auto_ptrs because they don't meet a fundamental requirement for container elements. That is, after a copy or an assignment of an auto_ptr is made, source and destination are not equivalent. In fact, the source auto_ptr gets modified because its value gets transferred and not copied. In practice, this means that sorting or even printing the elements of a container might destroy them. So, do not use auto.ptrs as container elements (if you have a standard-conforming C++ system, you will get an error at compile time if you try to use an auto_ptr as a container element).

To get reference semantics for STL containers you must write your own smart pointer class. But be aware: Even if you use a smart pointer with reference counting (a smart pointer that destroys its value automatically when the last reference to it gets destroyed), it is troublesome. For example, if you have direct access to the elements, you can modify their values while they are in the container. Thus, you could break the order of elements in an associative container. You don't want to do this.

In particular, it shows a possible way to implement reference semantics for STL containers by using smart pointers with reference counting.

No comments:

Post a Comment