|
 |
 |
[an error occurred while processing this directive]
- 3. Fill out the declaration of the pointer class. The class as implemented in the sample code assumes its pointer to the ObjectContainer object is always valid. If youre paranoid, feel free to add code that checks the validity of the pointer whenever referenced, and perhaps throw an exception to provide feedback during the debugging cycle. An invalid pointer could indicate a bug in the reference handling code somewhere. The following listing shows the filled-out ObjectPtr class (from memclass.hpp):
template <class t>
class ObjectPtr
{
ObjectContainer<t> *PointTo ;
public:
ObjectPtr( ObjectContainer<t> *source ) ;
ObjectPtr( const ObjectPtr<t> &copyfrom ) ;
ObjectPtr<t>& operator=( const ObjectPtr<t> &copyfrom ) ;
~ObjectPtr() ;
// data access
t* operator->(){ return PointTo->_data ; }
t& operator* (){ return *(PointTo->_data) ; }
} ;
// end class definition
- 4. Implement the inline functions. In How-To 13.2, functions that could not be inlined by the compiler were left in the header file for the sake of brevity. In this case, the two functions are broken out and put into their own header file to demonstrate the difference (and to get rid of the annoying warning messages cannot pre-compile headers ... code in header that some compilers spit out).
Generally, compilers cannot inline functions that contain loops; there are other general rules about what compilers can and cant inline, but some are compiler dependent. Let the compiler worry about whether it can inline something or not because many things a compiler can (and will, if you tell it to) inline have a negative performance impact on your program. Rather, concentrate on making sure code you know should not be inlined (because of length, generally) is not declared inline. The following listing shows the inline functions for the ObjectContainer and ObjectPtr classes from memclass.hpp:
//--------------------------------------------------------------
// In-line functions for ObjectContainer
//--------------------------------------------------------------
template <class t>
inline
ObjectContainer<t>::ObjectContainer( t* data, int array_size )
{
_data = data ;
ArraySize = array_size ;
Refs = 0 ;
}
// these two functions are the core functionality of the code,
// and because they deal with the actual management of the data
// must be protected from other threads
template <class t>
inline
void ObjectContainer<t>::reference()
{
// lock mutex here
Refs++ ;
// and release mutex
}
template <class t>
inline
void ObjectContainer<t>::dereference()
{
// lock mutex here
Refs-- ;
// copy mutex to local variable
if( Refs == 0 ) delete this ;
// release mutex though local variable (this has been
// destroyed, and so the mutex data member is no longer valid
}
template <class t>
inline
ObjectContainer<t>::~ObjectContainer()
{
if( ArraySize > 1 ) delete[] _data ;
else delete _data ;
// now throw an exception if we still have objects referring to us...
if( Refs ) throw logic_error( ObjectContainer destructor called while \
referenced.) ;
}
//--------------------------------------------------------------
// In-line functions for ObjectPtr
//--------------------------------------------------------------
template <class t>
inline
ObjectPtr<t>::ObjectPtr( ObjectContainer<t> *source )
{
source->reference() ;
PointTo = source ;
}
template <class t>
inline
ObjectPtr<t>::ObjectPtr( const ObjectPtr<t> &copyfrom )
{
copyfrom.PointTo->reference() ;
PointTo = copyfrom.PointTo ;
}
template <class t>
inline
ObjectPtr<t>& ObjectPtr<t>::operator=( const ObjectPtr<t>
⇒&copyfrom )
{
// handle possible self-assignment
if( this == &copyfrom ) return *this ;
PointTo->dereference() ;
copyfrom.PointTo->reference() ;
PointTo = copyfrom.PointTo ;
return *this ;
}
template <class t>
inline
ObjectPtr<t>::~ObjectPtr()
{
PointTo->dereference() ;
}
The noteworthy items in the code are the reference() and dereference() member functions. These require mutexes when running in multithreaded programs to prevent an object from referencing the object while it is in the process of being deleted.
- 5. Implement the out of line functions. Because these are template functions, they cannot be placed into a separate .cpp module; the compiler will need them to generate code for programs that use this class. The following code lists the out of line functions defined in memclass.hpp:
// copy constructor and assignment operator are not inlined,
// and for non-template implementations should be in a
// separate module if used with a compiler that supports
// pre-compiled headers
template <class t>
ObjectContainer<t>::ObjectContainer( ObjectContainer<t>
⇒&copyfrom )
{
ArraySize = copyfrom.ArraySize ;
Refs = 0 ; // we are making a copy of the contained object
// and therefore have no references.
if( ArraySize > 1 )
{
// deal with an array
_data = new t[ArraySize] ;
for( int i = 0 ; i < ArraySize ; i++ )
_data[i] = copyfrom._data[i] ;
}
else
{
_data = new t ;
*_data = *copyfrom._data ;
}
}
template <class t>
ObjectContainer<t>& ObjectContainer<t>::operator=
( ObjectContainer<t> &copyfrom )
{
if( this == &copyfrom ) return *this ;
if( Refs ) // error condition: cannot assign to container
// that isbeing referred to
throw logic_error(Tried to assign object container
⇒with Refs.);
// prevent copyfrom from being deleted while were copying it
copyfrom.reference() ;
if( ArraySize > 1 )
delete[] _data ;
else
delete _data ;
ArraySize = copyfrom.ArraySize ;
if ( ArraySize > 1 )
{
_data = new t[ArraySize] ;
for( int i = 0 ; i < ArraySize ; i++ )
_data[i] = copyfrom._data[i] ;
}
else
{
_data = new t ;
*_data = *copyfrom._data ;
}
copyfrom.dereference() ;
return *this ;
}
// end of file
|