Zaawansowane CPP/Ćwiczenia 14: Zarządzanie pamięcią: Różnice pomiędzy wersjami

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Mirek (dyskusja | edycje)
Nie podano opisu zmian
Nie podano opisu zmian
 
(Nie pokazano 17 wersji utworzonych przez jednego użytkownika)
Linia 21: Linia 21:
  }
  }
}}
}}
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie</span><div class="mw-collapsible-content" style="display:none">
Przykładowe rozwiązanie znajduje się w pliku  [[media:Linked_pool.h | linked_pool.h]].
</div></div>
{{cwiczenie|3||
{{cwiczenie|3||


Linia 33: Linia 38:
</div></div>
</div></div>


{{cwiczenie|4||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie</span><div class="mw-collapsible-content" style="display:none">
 
Nasza klasa będzie szablonem przyjmującym jako parametr typ elementu,
Przerób nieznacznie alokator podany na wykładzie, tak
aby jego funkcje wypisywały informacje o tym co robią. Użyj go z różnymi typami
pojemników. Co możesz powiedzieć o sposobie przydziału pamięci dla różnych
kontenerów?
}}
{{cwiczenie|5||
 
W oparciu o <code><nowiki>linked_pool</nowiki></code> napisz własny alokator pamięci.
Do jakich pojemników będzie można go stosować?.
}}
 
 
 
 
 
 
 
 
 
 
 
 
 
Przykładowe rozwiązanie znajduje się w pliku
{mod12/exercises/linked_pool.h}<tt>linkedpool.h</tt>.
 
'''Rozwiązanie 3 '''
 
Nasza klasa będzie szablonem przyjmującym jako parametr typ elementu
dla których pamięć będzie przydzielana. Ponieważ potrzebujemy jednej
dla których pamięć będzie przydzielana. Ponieważ potrzebujemy jednej
puli dla wszystkich obiektów danej klasy, obiekt puli będzie składową
puli dla wszystkich obiektów danej klasy, obiekt puli będzie składową
statyczną tej klasy. Musimy zdecydować jak i kiedy będzie ustalana
statyczną tej klasy. Musimy zdecydować jak i kiedy będzie ustalana
ilość pamięci w puli: czy w czasie kompilacji czy w czasie wykonania?
ilość pamięci w puli: czy w czasie kompilacji, czy w czasie wykonania?
Załóżmy że chcemy ją przydzialać dynamicznie. Dlatego w klasie umieścimy  
Załóżmy, że chcemy ją przydzialać dynamicznie. Dlatego w klasie umieścimy  
wskaźnik do obiketu puli:  
wskaźnik do obiektu puli:  
   
   
template<class T> class linked_pool_new {
template<class T> class linked_pool_new {
  static linked_pool<T> *_pool;
  static linked_pool<T> *_pool;
 
Pamięć dla puli będzie przudzielona za pomocą statycznej funkcji:
Pamięć dla puli będzie przydzielona za pomocą statycznej funkcji:
 
 
  public:
  public:
  static void request(size_t n) {
  static void request(size_t n) {
    _pool <nowiki> =</nowiki>    new linked_pool<T>(n);
    _pool <nowiki> =</nowiki>    new linked_pool<T>(n);
  }
  }
 
która musi zostać wywołana przez stworzeniem jakich kolwiek obiektów
która musi zostać wywołana przed stworzeniem jakichkolwiek obiektów
klasy {T}. Operator {new} sprawdza ten warunek:
klasy <tt>T</tt>. Operator <tt>new</tt> sprawdza ten warunek:
    
    
   void *operator new(size_t size) throw (std::bad_alloc)
   void *operator new(size_t size) throw (std::bad_alloc)
Linia 92: Linia 68:
       return (void *)_pool->allocate();
       return (void *)_pool->allocate();
     };
     };
Całość kodu można zobaczyć w pliku
{mod12/exercise/linked_new.h}<tt>linkednew.h</tt>. 


Z klasy {linked_pool_new} korzystamy dziedzicząc z niej:
Całość kodu można zobaczyć w pliku [[media:Linked_new.h | linked_new.h]]. 
   
 
struct X : public linked_pool_new<X> {
Z klasy <tt>linked_pool_new</tt> korzystamy dziedzicząc z niej:
  char big[1000000];  
 
};
  struct X : public linked_pool_new<X> {
  char big[1000000];  
};<br>
main() {
  X *x <nowiki> =</nowiki>    new X;
}
</div></div>
 
{{cwiczenie|4||


main() {
Przerób nieznacznie alokator podany na wykładzie, tak
  X *x <nowiki> =</nowiki>    new X;
aby jego funkcje wypisywały informacje o tym co robią. Użyj go z różnymi typami
}
pojemników. Co możesz powiedzieć o sposobie przydziału pamięci dla różnych
kontenerów?
'''Rozwiązanie 4 '''
}}


Przykładowy alokator oparty o funkcje {malloc} jest
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie</span><div class="mw-collapsible-content" style="display:none">
zaimplementowany w {mod12/exercices/mallocator.h}<tt>mallocator.h</tt>.  Funkcje {allocate} i {deallocate} są tak
Przykładowy alokator oparty o funkcję <tt>malloc</tt> jest
zdefiniowane że wypisują informacje o rozmiarze i ilości alokowanych  
zaimplementowany w pliku [[media:Mallocator.h | mallocator.h]].  Funkcje <tt>allocate</tt> i <tt>deallocate</tt> są tak
zdefiniowane, że wypisują informacje o rozmiarze i ilości alokowanych  
elementów:
elementów:
 
   pointer allocate(size_type n, const_pointer <nowiki> =</nowiki>    0) {
   pointer allocate(size_type n, const_pointer <nowiki> =</nowiki>    0) {
     void* p <nowiki> =</nowiki>  malloc(n * sizeof(T));
     void* p <nowiki> =</nowiki>  malloc(n * sizeof(T));
Linia 119: Linia 101:
       throw std::bad_alloc();
       throw std::bad_alloc();
     return static_cast<pointer>(p);
     return static_cast<pointer>(p);
   }
   }<br>
 
   void deallocate(pointer p, size_type n) {  
   void deallocate(pointer p, size_type n) {  
   std::cerr<<"d "<<n<<" "<<sizeof(T)<<" "<<p<<std::endl;
   std::cerr<<"d "<<n<<" "<<sizeof(T)<<" "<<p<<std::endl;
   free(p); }
   free(p); }
 
Wykonując np. instrukcje:
Wykonując np. instrukcje:
   
   
Linia 133: Linia 114:
   std::cerr<<"list"<<std::endl;  
   std::cerr<<"list"<<std::endl;  
   std::list<int,malloc_allocator<int> > l(4);
   std::list<int,malloc_allocator<int> > l(4);
łatwo się przekonać że np. {vector} i {deque} alokują pamięć dla
wielu elementów na raz natomiast lista alokuje pamięć po jednym
elemencie.


'''Rozwiązanie 5 '''
łatwo się przekonać, że np. <tt>vector</tt> i <tt>deque</tt> alokują pamięć dla
wielu elementów na raz, natomiast lista alokuje pamięć po jednym elemencie.
</div></div>
 
{{cwiczenie|5||
 
W oparciu o <code><nowiki>linked_pool</nowiki></code> napisz własny alokator pamięci.
Do jakich pojemników będzie można go stosować?.
}}


Z powyższego ćwieczenia widać, że prosty alokator przydzielający
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie</span><div class="mw-collapsible-content" style="display:none">
Z powyższego ćwiczenia widać, że prosty alokator przydzielający
pamięć na pojedyncze elementy tego samego rozmiaru, będzie mogł być
pamięć na pojedyncze elementy tego samego rozmiaru, będzie mogł być
użyty tylko z tymi pojemnikami które alokują pamięć pojedenczo np. z
użyty tylko z tymi pojemnikami, które alokują pamięć pojedynczo, np. z
listą. Ale właśnie takie pojemniki mogą najbardziej skorzystać z
listą. Ale właśnie takie pojemniki mogą najbardziej skorzystać z
przyspieszenia swojego działania dzięki sprawniejszej alokacji i
przyspieszenia swojego działania dzięki sprawniejszej alokacji i
dealokacji pamięci.
dealokacji pamięci.


Implementując alokator oparty o {linked_pool} znów musimy uważać na
Implementując alokator oparty o <tt>linked_pool</tt> znów musimy uważać na sposób przydziału pamięci dla puli. Ponieważ musimy zapewnić równoważność iteratorów, każda lista musi alokować pamięć z tej samej puli. Pula będzie więc składową statyczną alokatora. Z powodu mechanizmu <tt>rebind</tt> nie możemy jednak teraz przydzialać pamięci dla puli dynamicznie, tak jak to robiliśmy w <tt>linked_pool_new</tt>. Dlatego rozmiar puli będzie podawany jako drugi argument szablonu alokatora:
sposób przydziału pamięci dla puli. Ponieważ musimy zapewnić
 
równoważność iteratorów każda lista musi alokować pamięć z tej samej
  template <class T,size_t N> class      pool_allocator {
puli. Pula będzie więc składową statyczną alokatora. Z powodu
  private:
mechanizmu {rebind} nie możemy jednak teraz przydzialać pamięci dla
static  linked_pool<T> _pool;   
puli dynamicznie, tak jak to robiliśmy w {linked_pool_new}. Dlatego
 
rozmiar puli będzie podawany jako drugi argument szablonu alokatora:
   
template <class T,size_t N> class      pool_allocator {
private:
static  linked_pool<T> _pool;   
Inicjalizacja puli wygląda następująco:
Inicjalizacja puli wygląda następująco:
   
 
template<class T,size_t N>  linked_pool<T> pool_allocator<T,N>::_pool(N);   
  template<class T,size_t N>  linked_pool<T> pool_allocator<T,N>::_pool(N);   
 
(składowe statczne inicjalizowane są poza klasą).  
(składowe statyczne inicjalizowane są poza klasą). Wyrażenie <tt>rebind</tt> ma teraz postać:
Wyrażenie {rebind} ma teraz postać:
   
   
  template <class U>  
  template <class U>  
  struct rebind { typedef pool_allocator<U,N> other; };
  struct rebind { typedef pool_allocator<nowiki><U,N></nowiki> other; };
 
Całość kodu jest zamieszczona w pliku  
Całość kodu jest zamieszczona w pliku [[media:Pool_allocator.h | pool_allocator.h]]. Z tak zdefiniowanego alokatora korzysta się następująco:
{mod12/exercices/pool_allocator.h}<tt>poolallocator.h</tt>.
 
Z tak zdefiniowanego alokatora korzysta się następująco:
  std::list<int,pool_allocator<int,10000> > l;
   
</div></div>
  std::list<int,pool_allocator<int,10000> > l;

Aktualna wersja na dzień 10:51, 2 paź 2006

Ćwiczenie 1

Przerób przykłady z wykładu.

Ćwiczenie 2

Napisz własną implementację puli pamięci opartą o listę. Zasobnik powinien być szablonem przyjmującym jako parametr typ obiektów, dla których będzie przydzielał pamięć. Wielkość puli powinna być podawana w konstruktorze. Jeśli zażądamy za dużo pamięci, to powinien zostać rzucony wyjątek std::bad_alloc. Jeśli wyczerpie się pamięć w puli, żądanie przydziału powinno rzucić std::bad_alloc:

template<typename T> linked_pool {
linked_pool(size_t n) throw(std::bad_alloc); void *allocate() throw(std::bad_alloc); /*przydziela pamięc na jeden obiekt T*/ void deallocate(void *p) throw ();
release() throw (); /*zwalnia całą pamięć z puli*/ ~linked_pool() throw() ; }
Rozwiązanie

Ćwiczenie 3

W oparciu o linked_pool zaimplementuj klasę z własnymi operatorami new i delete. Zaimplementuj klasę tak, aby można było z niej dziedziczyć i w ten sposób łatwo implementować new i delete w dowolnej klasie.

Wskazówka
Rozwiązanie

Ćwiczenie 4

Przerób nieznacznie alokator podany na wykładzie, tak aby jego funkcje wypisywały informacje o tym co robią. Użyj go z różnymi typami pojemników. Co możesz powiedzieć o sposobie przydziału pamięci dla różnych kontenerów?

Rozwiązanie

Ćwiczenie 5

W oparciu o linked_pool napisz własny alokator pamięci. Do jakich pojemników będzie można go stosować?.

Rozwiązanie