Klasy - uchwyty
W celu zapewnienia lepszej obsługi obiektów określonej klasy, jej definicję dzielimy na dwie części.
Korzyści:
Prostszy i bardziej czytelny interfejs użytkownika do właściwej reprezentacji obiektu,
Dobra, stabilna kontrola dostępu poprzez uchwyt,
Możliwość zarządzania pamięcią poprzez uchwyt na rzecz reprezentacji,
Przekazywanie uchwytu w całości zamiast wskazania lub referencji do obiektu.
Przykład:
// wzorzec abstrakcyjnej klasy zbiorczej dla różnego
// typu zbiorów
template < class T >
class ZBIOR {
friend class ZBIOR_UCHWYT;
protect:
int uchwyt_licznik; // liczba aktywnych uchwytów
// danego zbioru
public:
virtual void wstaw( T * ) = 0;
virtual void usun( T * ) = 0;
virtual int nalezy_do( T * ) = 0;
virtual T * pierwszy( ) = 0;
virtual T * nastepny( ) = 0;
// . . .
ZBIOR( void ): uchwyt_licznik( 0 ) { } // konstruktor
};
class ZBIOR_UCHWYT {
ZBIOR * rep;
public:
// poniżej operator dostępu do zbioru
ZBIOR * operator → ( ) { return rep; }
// poniżej dwa konstruktory uchwytów
ZBIOR_UCHWYT( ZBIOR *pp ): rep( pp )
{ rep → uchwyt_licznik ++ ; }
ZBIOR_UCHWYT( const ZBIOR_UCHWYT & r ):
rep( r . rep ) { rep → uchwyt_licznik ++ ; }
// poniżej operator przypisania
ZBIOR_UCHWYT & operator =
( const ZBIOR_UCHWYT & r )
{ r . rep → uchwyt_licznik ++ ;
if( -- rep → uchwyt_licznik = = 0 ) delete rep ;
rep = r . rep ; return * this; }
~ ZBIOR_UCHWYT( )
{ if( -- rep → uchwyt_licznik = = 0 ) delete rep ; }
};
Zdefiniujmy jeszcze funkcję do związania uchwytu z nową reprezentacją
ZBIOR_UCHWYT:: void zwiąż( ZBIOR * pp )
{ pp → uchwyt_licznik ++ ;
if( -- rep → uchwyt_licznik = = 0 ) delete rep ;
rep = pp; }
"Jeżeli wszystkie odwołania do klasy ZBIOR przechodzą przez ZBIOR_UCHWYT, to użytkownik może zapomnieć o zarządzaniu pamięcią dla zbiorów."
Stroustrup
Dobrze jest zdefiniować wzorzec dla rodziny klas uchwytów
template < class T > class UCHWYT {
T * rep;
public:
T * operator →( ) { return rep; }
// . . .
};
Przykład użycia: class UCHWYT< ZBIOR > // . . .
Przykład takiego zdefiniowania wzorca, aby dawał użytkownikowi możliwość kontroli podczas każdego dostępu do reprezentacji, np. zliczał częstości dostępu do reprezentacji:
template < class T > class UCHWYT {
T * rep;
int licznik;
public:
T * operator →( ) { licznik ++; return rep; }
// . . .
};
Poniżej zdefiniujemy wzorzec klasy uchwytu tak, aby dawał użytkownikowi możliwość wykonywania pewnej pracy, na rzecz reprezentacji. Jeżeli praca ma być wykonywana zarówno przed-, jak i po dostępie, to sprawa jest bardziej złożona. W poniższym przykładzie pokazano, jak klasa uchwytu blokuje dostęp do zbioru na czas wstawiania i usuwania elementów. Takie blokowanie jest niezbędne przy współbieżności.
template < class T >
class ZBIOR_KONTROLER {
ZBIOR * rep;
public:
zablokuj( ); odblokuj( );
virtual void wstaw( T * p )
{ zablokuj( ); rep → wstaw( p ); odblokuj( ); }
virtual void usun( T * p )
{ zablokuj( ); rep → usun( p ); odblokuj( ); }
virtual int nalezy_do( T * p )
{ return nalezy_do( p ); }
virtual T * pierwszy( ) { return rep → pierwszy( p ); }
virtual T * nast( ) { return rep → nast( p ); }
};
Jak widać można blokować tylko niektóre funkcje. Będą blokowane tylko te, których funkcje zapowiadające przesunięto do klasy kontrolera.
71
reprezentacja
uchwyt