Last active
February 24, 2021 06:23
-
-
Save fenbf/0f3d404a57fe78278a2605d45b2ff2b8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// smart_ptr_deleters.cpp | |
// shows how to use custom deleters with unique_ptr and shared_ptr | |
// Bartlomiej Filipek, April 2016, bfilipek.com | |
#include <iostream> | |
#include <memory> | |
#include <functional> | |
#include <cassert> | |
// This class cannot be changed, might come from 3rd party library | |
// to delete it completely you need to call ReleaseElements first! | |
class LegacyList { | |
public: | |
LegacyList() { s_refCounter++; } | |
~LegacyList() { s_refCounter--; } | |
LegacyList(const LegacyList&) { s_refCounter++; } | |
LegacyList & operator= ( const LegacyList & ) { s_refCounter++; return *this; } | |
void Add(const std::string& str ) { } | |
void Remove(const std::string& str ) { } | |
void ReleaseElements() { } | |
static int GetGlobalObjectRefCount() { return s_refCounter; } | |
private: | |
static int s_refCounter; | |
}; | |
int LegacyList::s_refCounter = 0; | |
// old style Class, with explicit new/delete/raw pointers | |
class WordCache { | |
public: | |
WordCache() { m_pList = nullptr; } | |
~WordCache() { ClearCache(); } | |
void UpdateCache(LegacyList *pInputList) { | |
ClearCache(); | |
m_pList = pInputList; | |
if (m_pList) | |
{ | |
// do something with the list... | |
} | |
} | |
private: | |
void ClearCache() { if (m_pList) { m_pList->ReleaseElements(); delete m_pList; m_pList = nullptr; } } | |
LegacyList *m_pList; // owned by the object | |
}; | |
// stateless functor | |
struct LegacyListDeleterFunctor { | |
void operator()(LegacyList* p) { | |
p->ReleaseElements(); | |
delete p; | |
std::cout << "LegacyListDeleterFunctor..." << std::endl; | |
} | |
}; | |
// normal free function used to delete LegacyList | |
void DeleteLegacyList(LegacyList* p) { | |
p->ReleaseElements(); | |
delete p; | |
std::cout << "DeleteLegacyList function..." << std::endl; | |
} | |
// Modern version of the cache, we use std::unique_ptr with custom deleter to handle deletion of LegacyList | |
// we can remove default constructor and destructor, since unique_ptr will do this for us | |
// also we could remove ClearCache method (since it was only for conveniance) | |
class ModernWordCache { | |
public: | |
// different options for custom deleters | |
using unique_legacylist_ptr_stdfunc = std::unique_ptr<LegacyList, std::function<void (LegacyList*)>>; | |
using unique_legacylist_ptr_functor = std::unique_ptr<LegacyList, LegacyListDeleterFunctor>; | |
using unique_legacylist_ptr_function = std::unique_ptr<LegacyList, void (*)(LegacyList *)>; | |
using unique_legacylist_ptr = unique_legacylist_ptr_functor; | |
public: | |
void UpdateCache(unique_legacylist_ptr pInputList) { | |
m_pList = std::move(pInputList); | |
// do something with the list... | |
} | |
private: | |
//unique_legacylist_ptr m_pList { nullptr, &DeleteLegacyList }; | |
unique_legacylist_ptr m_pList; | |
}; | |
// Modern version that uses shared_ptr | |
class ModernSharedWordCache { | |
public: | |
void UpdateCache(std::shared_ptr<LegacyList> pInputList) { | |
m_pList = pInputList; | |
// do something with the list... | |
} | |
private: | |
std::shared_ptr<LegacyList> m_pList; | |
}; | |
void TestOldWay() | |
{ | |
WordCache myTestClass; | |
LegacyList* pList = new LegacyList(); | |
// fill the list... | |
myTestClass.UpdateCache(pList); | |
LegacyList* pList2 = new LegacyList(); | |
// fill the list | |
myTestClass.UpdateCache(pList2); | |
} | |
void TestUniqueModernWay() | |
{ | |
ModernWordCache myModernClass; | |
// whe nunique_legacylist_ptr uses a function, then we have to pass it in constructor | |
// ModernWordCache::unique_legacylist_ptr pUniqueList(new LegacyList(), &DeleteLegacyList); | |
// when nunique_legacylist_ptr uses a functor, functor object is created by default... | |
ModernWordCache::unique_legacylist_ptr pUniqueList(new LegacyList()); | |
myModernClass.UpdateCache(std::move(pUniqueList)); | |
//ModernWordCache::unique_legacylist_ptr pUniqueList2(new LegacyList(), &DeleteLegacyList); | |
ModernWordCache::unique_legacylist_ptr pUniqueList2(new LegacyList()); | |
myModernClass.UpdateCache(std::move(pUniqueList2)); | |
} | |
void TestSharedModernWay() | |
{ | |
ModernSharedWordCache mySharedClass; | |
mySharedClass.UpdateCache(std::shared_ptr<LegacyList>(new LegacyList(), LegacyListDeleterFunctor())); | |
mySharedClass.UpdateCache(std::shared_ptr<LegacyList>(new LegacyList(), DeleteLegacyList)); | |
// simple example with custom deleter for <int> | |
std::shared_ptr<int> pIntPtr(new int(10), [](int *pi) { delete pi; }); | |
} | |
int main() | |
{ | |
std::cout << "sizeof(int*) = " << sizeof(int*) << '\n'; | |
// size of unique_ptr will be 4 or 8 bytes | |
std::cout << "sizeof(std::unique_ptr<LegacyList>) = " << sizeof(std::unique_ptr<LegacyList>) << '\n'; | |
// std::function is a bit heavy, around 32 bytes! (on x64) | |
std::cout << "sizeof(std::function<void (LegacyList*)>) = " << sizeof(std::function<void (LegacyList*)>) << '\n'; | |
std::cout << "sizeof(unique_legacylist_ptr_stdfunc) = " << sizeof(ModernWordCache::unique_legacylist_ptr_stdfunc) << '\n'; | |
// with normal function pointer we have two pointers to store, so 8 or 16 bytes | |
std::cout << "sizeof(unique_legacylist_ptr_function) = " << sizeof(ModernWordCache::unique_legacylist_ptr_function) << '\n'; | |
// if we use stateless functor then the compiler will probably use Empty Base Class optimization and thus we use only 8 bytes(like normal unique_ptr) !! | |
std::cout << "sizeof(unique_legacylist_ptr_functor) = " << sizeof(ModernWordCache::unique_legacylist_ptr_functor) << '\n'; | |
// shared_ptr does not include deleter in type definition, size is always the same | |
std::cout << "sizeof(std::shared_ptr<LegacyList>) = " << sizeof(std::shared_ptr<LegacyList>) << '\n'; | |
auto IntDel = [](int *p) { delete p; }; | |
std::cout << "sizeof(std::unique_ptr<int, decltype(IntDel)>) = " << sizeof(std::unique_ptr<int, decltype(IntDel)>) << '\n'; | |
assert(LegacyList::GetGlobalObjectRefCount() == 0); | |
TestOldWay(); | |
assert(LegacyList::GetGlobalObjectRefCount() == 0); | |
assert(LegacyList::GetGlobalObjectRefCount() == 0); | |
TestUniqueModernWay(); | |
assert(LegacyList::GetGlobalObjectRefCount() == 0); | |
assert(LegacyList::GetGlobalObjectRefCount() == 0); | |
TestSharedModernWay(); | |
assert(LegacyList::GetGlobalObjectRefCount() == 0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment