AutomaticHandle ciąg dalszy
Jak się okazuje nic nie jest na początku idealne. Dzięki uwagom Revo udało mi się usprawnić szablon automatycznych uchwytów. Revo poradził mi dodanie dodatkowego wskaźnika na licznik odwołań do danego zasobu. Dzięki temu możliwe było usunięcie jednej funkcji wirtualnej, a druga została sprowadzona do roli sprzątaczki, która zostaje wywołana tylko w momencie, gdy licznik osiąga wartość 0. Dodatkowo dodałem sprawdzenie czy uchwyt nie jest zerowy przed odwołaniem się do wskaźników.
Poniżej znajduję się kod poprawionej klasy szablonowej AutomaticHandle:
typedef unsigned int Dword; template< typename TAG> class AutomaticHandle { public: // Podstawowy konstruktor AutomaticHandle(NLib::Dword handle, MHandleMgr* manager, NLib::Dword* resCounter) : m_handle(handle), m_manager(manager), m_resCounter(resCounter) { if(m_handle) ++(*m_resCounter); } // Konstruktor kopiujący AutomaticHandle(const AutomaticHandle& src) : m_manager(src.m_manager), m_handle(src.m_handle), m_resCounter(src.m_resCounter) { if(m_handle) ++(*m_resCounter); } // Destruktor ~AutomaticHandle() { if(m_handle) { if(!(--(*m_resCounter))) m_manager->ReleaseResource(m_handle); } } // Operator przypisania const AutomaticHandle& operator =(const AutomaticHandle& src) { if(m_handle) { if(!(--(*m_resCounter))) m_manager->ReleaseResource(m_handle); } m_handle = src.m_handle; if(m_handle) ++(*(m_resCounter = src.m_resCounter)); return src; } // Operator równości bool operator ==(const AutomaticHandle& src) { return m_handle == src.m_handle; } // Operator różności bool operator !=(const AutomaticHandle& src) { return m_handle != src.m_handle; } // Operator Dword operator NLib::Dword() const { return m_handle; } // Operator bool operator bool() const { return !!m_handle; } private: // Uchwyt NLib::Dword m_handle; // Wskaźnik do managera MHandleMgr* m_manager; // Wskaźnik do licznika zasobu NLib::Dword* m_resCounter; }; |
Kolejną klasą jest AutoHandleMgr, czyli klasa implementująca obsługę automatycznych uchwytów. Aktualnie tylko trzy funkcje są dostępne klasie pochodnej, w tym jedna dla uchwytów. Klasa ta posiada jedną funkcję abstrakcyjną, w której po zdefiniowaniu w klasie pochodnej należy usuwać odpowiednie zasoby zgodnie z przekazanym indeksem.
Oto jej definicja:
typedef unsigned int Dword; class AutoHandleMgr { // Unia do wyciągania informacji z uchwytu enum { // Rozmiary pól MAX_BITS_INDEX = sizeof(NLib::Dword) * 4, MAX_BITS_MAGIC = sizeof(NLib::Dword) * 4, // Maksymalne wartości pól MAX_INDEX = (1 << MAX_BITS_INDEX) - 1, MAX_MAGIC = (1 << MAX_BITS_MAGIC) - 1 }; public: // Funkcja do zwracania uchwytu void ReleaseResource(NLib::Dword handle) { NAssert(GetIndex(handle) < m_magicValues.size(), "Wrong Handle"); NAssert(CheckMagic(handle), "Wrong handle"); DeleteHandleAt(GetIndex(handle)); ReleaseResourceByIndex(GetIndex(handle)); } protected: // Funkcja tworzy nowy uchwyt i go zwraca // Index zawarty w uchwycie odpowiada indeksowi w tablicy z danymi // w odziedziczonej klasie NLib::Dword CreateNewHandle() { NLib::Dword handle; static NLib::Dword s_autoMagic = 0; if(++s_autoMagic > MAX_MAGIC) s_autoMagic = 1; // 0 oznacza pusty uchwyt // Jeżeli nie ma wolnych miejsc to tworze nowy index if(m_freeSlots.empty()) { handle = m_magicValues.size() << (MAX_BITS_INDEX - 1); m_magicValues.push_back(s_autoMagic); } else { handle = m_freeSlots.back() << (MAX_BITS_INDEX - 1); m_magicValues[m_freeSlots.back()] = s_autoMagic; m_freeSlots.pop_back(); } handle |= s_autoMagic; return handle; } // Funkcja zwraca index NLib::Dword GetIndex(NLib::Dword handle) { return (handle >> (MAX_BITS_INDEX - 1)) & MAX_INDEX; } private: // Funkcja kasuje wskazany uchwyt void DeleteHandleAt(NLib::Dword index) { m_freeSlots.push_back(index); m_magicValues[index] = 0; } // Funkcja zwraca część magiczną NLib::Dword GetMagic(NLib::Dword handle) { return handle & MAX_MAGIC; } // Funkcja sprawdzająca wartośc magic bool CheckMagic(NLib::Dword handle) { NAssert(GetIndex(handle) < m_magicValues.size(), "Wrong Handle"); return GetMagic(handle) == m_magicValues[GetIndex(handle)]; } private: // Funkcja dekrementująca licznik w zasobach virtual void ReleaseResourceByIndex(NLib::Dword index) = 0; private: // Vector zawierający magiczne wartości std::vector m_magicValues; // Vector zawierający indeksy z wolnymi miejscami std::vector m_freeSlots; }; |
W przypadku tego menadżera uchwyty muszą być usunięte przed nim, inaczej pojawią się błędy z dostępem do pamięci.