Created
May 23, 2017 15:03
-
-
Save mpenick/88f8b0574ea570051112a8df8cfa707d to your computer and use it in GitHub Desktop.
Overriding C++ Allocators
This file contains hidden or 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
| #include <new> | |
| #include <iostream> | |
| // Approach #1: Overload global operators | |
| // | |
| // Problem: Our allocator overrides/conflicts with the application's | |
| // allocator when static linking. | |
| void* operator new(size_t size) throw(std::bad_alloc) { | |
| void* p = malloc(size); | |
| printf("new %p\n", p); | |
| return p; | |
| } | |
| void* operator new[](size_t size) throw(std::bad_alloc) { | |
| void* p = malloc(size); | |
| printf("new[] %p\n", p); | |
| return p; | |
| } | |
| void operator delete(void* ptr) throw() { | |
| printf("delete %p\n", ptr); | |
| free(ptr); | |
| } | |
| void operator delete[](void* ptr) throw() { | |
| printf("delete[] %p\n", ptr); | |
| free(ptr); | |
| } | |
| class MyClass { | |
| public: | |
| MyClass() { | |
| std::cout << "Constructor" << std::endl; | |
| } | |
| ~MyClass() { | |
| std::cout << "Destructor" << std::endl; | |
| } | |
| }; | |
| int main() { | |
| std::cout << "Allocating single instance of POD type" << std::endl; | |
| int* i = new int; | |
| delete i; | |
| std::cout << std::endl << "Allocating an array of instances of POD type" << std::endl; | |
| int* ia = new int[4]; | |
| delete[] ia; | |
| std::cout << std::endl << "sizeof(MyClass) = " << sizeof(MyClass) << std::endl; | |
| std::cout << std::endl << "Allocating single instance of MyClass" << std::endl; | |
| MyClass* c = new MyClass; | |
| delete c; | |
| std::cout << std::endl << "Allocating an array of instances of MyClass" << std::endl; | |
| MyClass* ca = new MyClass[4]; | |
| delete[] ca; | |
| } |
This file contains hidden or 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
| #include <new> | |
| #include <iostream> | |
| // Approach #2: Use class-level overloads | |
| // | |
| // Problem: This doesn't work for POD types. | |
| // | |
| // Problem: It forces all classes to have to subclass "Allocated" to work | |
| // properly. This can be error prone because it's hard to ensure that all | |
| // existing and future classes will do this correctly. | |
| // | |
| class Allocated { | |
| public: | |
| void* operator new(size_t size) { | |
| void* p = malloc(size); | |
| printf("Allocated::new %p\n", p); | |
| return p; | |
| } | |
| void* operator new[](size_t size) { | |
| void* p = malloc(size); | |
| printf("Allocated::new[] %p\n", p); | |
| return p; | |
| } | |
| void operator delete(void* ptr) { | |
| printf("Allocated::delete %p\n", ptr); | |
| free(ptr); | |
| } | |
| void operator delete[](void* ptr) { | |
| printf("Allocated::delete[] %p\n", ptr); | |
| free(ptr); | |
| } | |
| }; | |
| class MyClass : public Allocated { | |
| public: | |
| MyClass() { | |
| std::cout << "Constructor" << std::endl; | |
| } | |
| ~MyClass() { | |
| std::cout << "Destructor" << std::endl; | |
| } | |
| }; | |
| int main() { | |
| std::cout << "Allocating single instance of POD type" << std::endl; | |
| int* i = new int; | |
| delete i; | |
| std::cout << std::endl << "Allocating an array of instances of POD type" << std::endl; | |
| int* ia = new int[4]; | |
| delete[] ia; | |
| std::cout << std::endl << "sizeof(MyClass) = " << sizeof(MyClass) << std::endl; | |
| std::cout << std::endl << "Allocating single instance of MyClass" << std::endl; | |
| MyClass* c = new MyClass; | |
| delete c; | |
| std::cout << std::endl << "Allocating an array of instances of MyClass" << std::endl; | |
| MyClass* ca = new MyClass[4]; | |
| delete[] ca; | |
| } |
This file contains hidden or 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
| #include <new> | |
| #include <iostream> | |
| // Approach #3: Use placement new overload | |
| // | |
| // Problem: This doesn't work. Our overloaded `operator delete` and `operator | |
| // delete[]` functions are never called and there's no syntax to call them e.g. | |
| // `delete (Alloc) foo` or `delete[] (Alloc) foo`. The syntax `operator delete(ptr, ALLOC)` | |
| // exists but this doesn't work for the array syntax `operator delete[](ptr, ALLOC)` because | |
| // `new SomeType[]` can add extra memory keep track of the number of elements in the array so | |
| // the elements can be deconstructed properly. | |
| struct AllocTag { }; | |
| const AllocTag ALLOC = { }; | |
| void* operator new(size_t size, AllocTag) throw(std::bad_alloc) { | |
| void* p = malloc(size); | |
| printf("new (ALLOC) %p\n", p); | |
| return p; | |
| } | |
| void* operator new[](size_t size, AllocTag) throw(std::bad_alloc) { | |
| void* p = malloc(size); | |
| printf("new (ALLOC) [] %p\n", p); | |
| return p; | |
| } | |
| void operator delete(void* ptr, AllocTag) throw() { | |
| printf("delete (ALLOC) %p\n", ptr); | |
| free(ptr); | |
| } | |
| void operator delete[](void* ptr, AllocTag) throw() { | |
| printf("delete (ALLOC) [] %p\n", ptr); | |
| free(ptr); | |
| } | |
| class MyClass { | |
| public: | |
| MyClass() { | |
| std::cout << "Constructor" << std::endl; | |
| } | |
| ~MyClass() { | |
| std::cout << "Destructor" << std::endl; | |
| } | |
| }; | |
| int main() { | |
| std::cout << "Allocating single instance of POD type" << std::endl; | |
| int* i = new (ALLOC) int; | |
| delete i; | |
| std::cout << std::endl << "Allocating an array of instances of POD type" << std::endl; | |
| int* ia = new (ALLOC) int[4]; | |
| delete[] ia; | |
| std::cout << std::endl << "sizeof(MyClass) = " << sizeof(MyClass) << std::endl; | |
| std::cout << std::endl << "Allocating single instance of MyClass" << std::endl; | |
| MyClass* c = new (ALLOC) MyClass; | |
| delete c; | |
| std::cout << std::endl << "Allocating an array of instances of MyClass" << std::endl; | |
| MyClass* ca = new (ALLOC) MyClass[4]; | |
| delete[] ca; | |
| } |
This file contains hidden or 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
| #include <new> | |
| #include <iostream> | |
| #include <stdint.h> | |
| // Approach #4: Create a custom allocator | |
| // | |
| // Problem: Complexity | |
| class Memory { | |
| public: | |
| template <class T> | |
| static T* allocate() { | |
| T* ptr = reinterpret_cast<T*>(malloc(sizeof(T))); | |
| return new (ptr) T(); | |
| } | |
| template <class T, class Arg1> | |
| static T* allocate(const Arg1 arg1) { | |
| T* ptr = reinterpret_cast<T*>(malloc(sizeof(T))); | |
| return new (ptr) T(arg1); | |
| } | |
| template <class T, class Arg1, class Arg2> | |
| static T* allocate(const Arg1& arg1, const Arg2& arg2) { | |
| T* ptr = reinterpret_cast<T*>(malloc(sizeof(T))); | |
| return new (ptr) T(arg1, arg2); | |
| } | |
| template <class T, class Arg1, class Arg2, class Arg3> | |
| static T* allocate(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) { | |
| T* ptr = reinterpret_cast<T*>(malloc(sizeof(T))); | |
| return new (ptr) T(arg1, arg2, arg3); | |
| } | |
| template <class T, class Arg1, class Arg2, class Arg3, class Arg4> | |
| static T* allocate(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4) { | |
| T* ptr = reinterpret_cast<T*>(malloc(sizeof(T))); | |
| return new (ptr) T(arg1, arg2, arg3, arg4); | |
| } | |
| template <class T> | |
| static void deallocate(T* ptr) { | |
| ptr->~T(); | |
| free(ptr); | |
| } | |
| static void* malloc(size_t size) { | |
| // The alloc callback would be used here | |
| return ::malloc(size); | |
| } | |
| static void* realloc(void* ptr, size_t size) { | |
| // The alloc callback would be used here | |
| return ::realloc(ptr, size); | |
| } | |
| static void* calloc(size_t count, size_t size) { | |
| // The alloc callback would be used here | |
| return ::calloc(size, count); | |
| } | |
| static void free(void* ptr) { | |
| // The alloc callback would be used here | |
| return ::free(ptr); | |
| } | |
| }; | |
| template <class T> | |
| class DynamicArray { | |
| public: | |
| DynamicArray(size_t n) | |
| : n_(n) | |
| , elements_(reinterpret_cast<T*>(Memory::malloc(sizeof(T) * n_))) { | |
| for (size_t i = 0; i < n_; ++i) { | |
| new (elements_ + i) T(); | |
| } | |
| } | |
| DynamicArray(const DynamicArray& other) | |
| : n_(other.n_) | |
| , elements_(reinterpret_cast<T*>(Memory::malloc(sizeof(T) * n_))) { | |
| for (size_t i = 0; i < n_; ++i) { | |
| new (elements_ + i) T(other[i]); | |
| } | |
| } | |
| ~DynamicArray() { | |
| for (size_t i = 0; i < n_; ++i) { | |
| (elements_ + i)->~T(); | |
| } | |
| Memory::free(elements_); | |
| } | |
| T& operator[](size_t index) { | |
| return *(elements_ + index); | |
| } | |
| const T& operator[](size_t index) const { | |
| return *(elements_ + index); | |
| } | |
| T* data() { return elements_; } | |
| const T* data() const { return elements_; } | |
| private: | |
| size_t n_; | |
| T* elements_; | |
| }; | |
| class MyClass { | |
| public: | |
| MyClass() { | |
| std::cout << "Constructor" << std::endl; | |
| } | |
| ~MyClass() { | |
| std::cout << "Destructor" << std::endl; | |
| } | |
| private: | |
| int a; | |
| char b; | |
| }; | |
| int main() { | |
| std::cout << "Allocating single instance of POD type" << std::endl; | |
| int* i = Memory::allocate<int>(); | |
| Memory::deallocate(i); | |
| std::cout << std::endl << "Allocating an array of instances of POD type" << std::endl; | |
| DynamicArray<int> ia(4); | |
| std::cout << std::endl << "sizeof(MyClass) = " << sizeof(MyClass) << std::endl; | |
| std::cout << std::endl << "Allocating single instance of MyClass" << std::endl; | |
| MyClass* c = Memory::allocate<MyClass>(); | |
| Memory::deallocate(c); | |
| std::cout << std::endl << "Allocating an array of instances of MyClass" << std::endl; | |
| DynamicArray<MyClass> ca0(4); | |
| DynamicArray<MyClass> ca1(ca0); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment