I am writing a custom memory manager in C++ to monitor "new" and "delete" usage.
In my code, I have overloaded new, new[], delete, delete[] operators and am using a STL map to hold the addresses of the allocated memory as key values and storing the number of bytes allocated as mapped values. I also update byte_counter and number_of_allocations.
I keep receiving the error "Thread 1: EXC_BAD_ACCESS (code=2, address=0x7fff5f3fffd8)" (I am using XCode as my IDE) that takes me to this piece of code:
template <class _Tp, class _Compare, class _Allocator>
template <class ..._Args>
typename __tree<_Tp, _Compare, _Allocator>::__node_holder
__tree<_Tp, _Compare, _Allocator>::__construct_node(_Args&& ...__args)
{
__node_allocator& __na = __node_alloc();
__node_holder __h(__node_traits::allocate(__na, 1), _Dp(__na));
__node_traits::construct(__na, _VSTD::addressof(__h->__value_), _VSTD::forward<_Args>(__args)...);
__h.get_deleter().__value_constructed = true;
return __h;
}
Here is my code:
#include <iostream>
#include <map>
#include <cstdlib>
using namespace std;
/********************************************************************************/
void* operator new (size_t st);
void* operator new [](size_t st);
void operator delete(void* p);
void operator delete [](void* p);
/********************************************************************************/
class DynamicMemoryManager
{
private:
static int number_of_allocations;
static size_t total_bytes;
static map<const void*, const size_t> mem_map;
public:
DynamicMemoryManager();
static DynamicMemoryManager* create();
static void destroy();
static void print();
static void add(const void* p, const size_t size);
static void remove(const void* p);
};
int DynamicMemoryManager::number_of_allocations = 0;
size_t DynamicMemoryManager::total_bytes = 0;
map<const void*, const size_t> DynamicMemoryManager::mem_map;
DynamicMemoryManager::DynamicMemoryManager() {
number_of_allocations = 0;
total_bytes = 0;
mem_map.clear();
}
void DynamicMemoryManager::print() {
cout << "number_of_allocations: " << number_of_allocations << endl;
cout << "total_bytes: " << total_bytes << endl;
}
void DynamicMemoryManager::add(const void* p, const size_t size) {
number_of_allocations++;
total_bytes += size;
mem_map.insert(pair<const void*, const size_t>(p, size));
}
void DynamicMemoryManager::remove(const void*p) {
size_t sz = mem_map[p];
number_of_allocations--;
total_bytes -= sz;
mem_map.erase(p);
}
/********************************************************************************/
int main()
{
DynamicMemoryManager::print();
int* i = new int(88);
double* d = new double(8.8);
string* s = new string("8888");
char* c = new char('8');
DynamicMemoryManager::print();
delete i;
delete d;
delete s;
delete c;
return 0;
}
/********************************************************************************/
void* operator new (size_t st) {
void* p = malloc(st);
DynamicMemoryManager::add(p, st);
return p;
}
void* operator new [](size_t st) {
void* p = malloc(st);
DynamicMemoryManager::add(p, st);
return p;
}
void operator delete(void* p) {
free(p);
DynamicMemoryManager::remove(p);
}
void operator delete [](void* p) {
free(p);
DynamicMemoryManager::remove(p);
}
I am not sure why my code keeps coming up with BAD_ACCESS errors. How should I design my code so that it successfully updates the STL map with each NEW and DELETE use? Please help, and thank you!
EDIT: I found that the error is coming from my overloaded NEW operator infinitely recursing. My professor hints that we should refer to void example5() in this piece of code. I'm still not sure exactly what I'm missing in my code to prevent my overloaded NEW from recursing :
#include <array>
#include <map>
#include <functional>
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <regex>
#include <chrono>
#include <ctime>
#include <vector>
#include <memory>
using namespace std::chrono;
using namespace std;
array<string, 12> smonths =
{
"january",
"february",
"march",
"april",
"may",
"june",
"july",
"august",
"september",
"october",
"november",
"december"
};
vector<string> Parse(string text, string split)
{
vector<string> words;
sregex_token_iterator end;
regex pattern(split);
for (sregex_token_iterator pos(text.begin(), text.end(), pattern); pos != end; ++pos)
{
if ((*pos).length() > 0)
{
if ((static_cast<string>(*pos))[0] != 0x20)
words.push_back(*pos);
}
}
return words;
}
int getHash(string s)
{
hash<string> hstring;
return hstring(s);
}
struct KeyValue : pair<string, int>
{
string key;
int value;
KeyValue(string s, int n) { first = s; second = n; }
KeyValue(const KeyValue& kv) { first = kv.first; second = kv.second; }
KeyValue(const KeyValue&& kv) { first = kv.first; second = kv.second; }
};
bool operator == (const KeyValue a, const KeyValue b)
{
return a.first.compare(b.first) == 0;
}
/*
If you think about how you usually allocate memory dynamically
(using the 'new' operator), you could ask why the STL provides
such a thing called allocator that does all the memory management
of the container classes. The concept of allocators was originally
introduced to provide an abstraction for different memory models
to handle the problem of having different pointer types on certain
16-bit operating systems (such as near, far, and so forth).
However, this approach failed. Nowadays, allocators serve as an
abstraction to translate the need to use memory into a raw call
for memory. Allocators simply separate the implementation of
containers, which need to allocate memory dynamically, from the
details of the underlying physical memory management. You can simply
apply different memory models such as shared memory, garbage
collections, and so forth to your containers without any difficulty
because allocators provide a common interface.
*/
template <typename T>
class MallocAllocator
{
public:
typedef T value_type;
MallocAllocator() {}
template <typename U> MallocAllocator(const MallocAllocator<U>& other) {}
T* allocate(size_t count)
{
return (T*)malloc(count * sizeof(T));
}
void deallocate(T* object, size_t n)
{
void* ptr = reinterpret_cast<void*>(object);
free(ptr);
}
};
MallocAllocator<void*> memoryManager;
void* operator new(size_t size)
{
//cout << "Allocating memory..." << endl;
auto newObject = memoryManager.allocate(size);
return newObject;
}
void operator delete(void* objectPtr) noexcept
{
void** ptr = reinterpret_cast<void**>(objectPtr);
memoryManager.deallocate(ptr, 0);
//free(objectPtr);
}
template <typename _Type = void>
struct Less
{ // functor for operator<
constexpr bool operator()(const _Type& _Left, const _Type& _Right) const
{
return (_Left < _Right);
}
};
void example5()
{
int* p = new int;
system_clock::time_point tbegin = system_clock::now();
map<string, int, Less<string>, MallocAllocator<int>> frequency;
ifstream infile("Words.txt");
while (!infile.eof())
{
string buffer;
getline(infile, buffer);
frequency[buffer] = 0;
}
infile.close();
infile.open("Speech.txt");
while (!infile.eof())
{
string buffer;
getline(infile, buffer);
vector<string> vs = Parse(buffer, "[a-zA-Z0-9]*");
for (string s : vs)
{
int& number = frequency[s];
++number;
}
}
ofstream outfile("Frequency.txt");
for (auto p : frequency)
{
if (p.second)
{
outfile << p.first << "\t" << p.second << endl;
cout << p.first << "\t" << p.second << endl;
}
}
outfile.close();
system_clock::time_point tend = system_clock::now();
cout << "Duration: " << static_cast<double>((tend - tbegin).count()) / 10000000.0 << endl;
}