C++NEW和DELETE监视器:重载NEW和DELETE

C++ NEW and DELETE Monitor: Overloaded NEW and DELETE

本文关键字:DELETE NEW 重载 监视器 C++NEW      更新时间:2023-10-16

我正在C++中编写一个自定义内存管理器来监视"new"answers"delete"的使用情况。

在我的代码中,我重载了new、new[]、delete、delete[]运算符,并使用STL映射将已分配内存的地址保存为键值,并将已分配的字节数存储为映射值。我还更新了byte_counter和number_of_allocations。

我一直收到错误"线程1:EXC_BAD_ACCESS(代码=2,地址=0x7fff5f3ffd8)"(我使用XCode作为IDE),这将导致我进入以下代码段:

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;
}

这是我的代码:

#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);
}

我不知道为什么我的代码总是出现BAD_ACCESS错误。我应该如何设计我的代码,以便它在每次使用NEW和DELETE时成功地更新STL映射?请帮忙,谢谢!

编辑:我发现错误来自于我重载的NEW操作符无限递归。我的教授暗示我们应该在这段代码中引用void example5()。我仍然不确定我的代码中到底缺少了什么,以防止过载的NEW再次出现:

#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;
}

我应该如何设计我的代码,以便它在每次使用NEW和DELETE时成功地更新STL映射?

不容易。CCD_ 1分配和释放动态存储器的方法,以存储和丢弃地图的元素。

它们使用Allocator的相应成员函数来分配和解除分配动态内存。默认情况下Allocatorstd::allocator<std::pair<const Key, T>> std::allocator通过调用全局operator new并通过调用全局operator delete(即要替换为受监控版本的操作符,这些操作符插入和删除元素您的std::map)。

因此,例如,当您的operator new开始在中插入第一个元素时映射,在插入操作中再次调用它,该操作将启动另一次插入操作等等,直到内存耗尽并崩溃(正如您所看到的)。

避免这种情况的唯一方法是将地图设为CCD_ 10,其中std::map<Key,T,Compare,Allocator>1是您自己编写的用于分配和解除分配的分配器动态存储器而不调用CCD_ 12或CCD_。

您可以尝试使用标准C库的mallocfree

但我劝你不要麻烦。如果你认为你的内存管理器会揭示堆的所有信息,你必须用什么方式来写就足够了向你表明它不会。它对自己的堆使用情况一无所知,也不了解通过处理堆的程序中链接的任何其他内容的堆使用情况CCD_ 16接口。看看堆会发生什么,解决方案是Valgrind对于OS X,请参阅Yosemite和Valgrind