Previous section   Next section

Imperfect C++ Practical Solutions for Real-Life Programming
By Matthew Wilson
Table of Contents
Chapter 26.  What's Your Address?


26.3. What Do We Return?

Another source of abuse in overload operator &() is in the type it returns. Since we can make it return anything, it's easy to have it return something bad; naturally, this is the case for any operator.

We saw in Chapter 14 some of the problems attendant in passing arrays of inherited types with functions that take pointers to the base type. There's another dimension to that nasty problem when overloading operator &(). Consider the following types:

Listing 26.3.


struct THING


{


  int i;


  int j;


};


struct Thing


{


  THING thing;


  int   k;





  THING *operator &()


  {


    return &thing;


  }


  THING const *operator &() const;


};



Now we're in the same position we would be if Thing inherited publicly from THING.



void func(THING *things, size_t cThings);





Thing things[10];





func(&things[0], dimensionof(things)); // Oop!!



By providing the operator &() overloads for "convenience," we've exposed ourselves to abuse of the Thing type. I'm not going to suggest the application of any of the measures described in Chapter 14 here, because I think overloading operator &() is just a big no-no.

A truly bizarre confluence of factors is the case where the operator is destructive—it releases the resources—and you are passing an array of (even correctly size) wrapper class instances to a function, as in Listing 26.4.

Listing 26.4.


struct ANOTHER


{


  . . .


};





void func(ANOTHER *things, size_t cThings);


inline void func(array_proxy<ANOTHER> const &things)


{


  func(things.base(), things.size());


}





class Another


{


  ANOTHER *operator &()


  {


    ReleaseAndReset(m_another);


    return &m_another;


  }


private:


  ANOTHER m_another;


};



Let's assume you're on your best behavior and are using an array_proxy (see section 14.5.5) and translator method to ensure that ANOTHER and Another can be used together.



Another  things[5];





. . . // Modify things





func(things); // sizeof(ANOTHER) must == sizeof(Another)



Irrespective of the semantics of func(), in calling the function things[0] will be reset and things[1]things[4] will not be affected. This is because the array constructor of array_proxy uses explicit array subscript syntax, as all good array manipulation code should (see Chapter 27). If you were to do it manually, you'd still need to apply the operator, unless Another inherited publicly from ANOTHER and you called the two parameter version of func() and relied on array decay (see section 14.2).

If func() does not change the contents of the array passed to it, then this supposedly benign call has the nasty side effect of destroying the first element passed to it. If func() modifies the contents of the array, then things[1]things[4] are subject to resource leaks, as their contents prior to the call are simply overwritten by func().


      Previous section   Next section