2016-05-14 25 views
0

Ich schreibe einen benutzerdefinierten Speichermanager in C++, um "neue" und "löschen" Verwendung zu überwachen.C++ NEU und DELETE Monitor: Überladen NEU und DELETE

In meinem Code habe ich neue, neue Operatoren [], delete, delete [] überladen und verwende eine STL-Map, um die Adressen des zugewiesenen Speichers als Schlüsselwerte zu speichern und die Anzahl der zugewiesenen Bytes als zugeordnete Werte zu speichern . Ich aktualisiere auch byte_counter und number_of_allocations.

Ich halte empfängt den Fehler "Thema 1: EXC_BAD_ACCESS (code = 2, Adresse = 0x7fff5f3fffd8)" (I XCode als meine IDE bin mit), die mich zu diesem Stück Code nimmt:

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

Hier ist mein 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); 
} 

Ich bin mir nicht sicher, warum mein Code immer mit BAD_ACCESS Fehler kommt. Wie soll ich meinen Code so gestalten, dass er die STL-Map bei jedem NEW- und DELETE-Einsatz erfolgreich aktualisiert? Bitte helfen und danke!

EDIT: Ich fand, dass der Fehler von meinem überladenen NEW-Operator kommt, der unendlich rekursiv ist. Mein Professor weist darauf hin, dass wir in diesem Codeabschnitt auf void example5() verweisen sollten. Ich bin immer noch nicht sicher, genau das, was ich in meinem Code bin fehlt meine überladene NEW von recursing zu verhindern:

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

Sie erkennen, dass 'std :: selbst map' verwendet' new'/'delete' selbst intern als Teil des Inhalts der Verwaltung der Karte, die scheinbar Ihre überladenen Operatoren aufruft, oder? –

Antwort

0

Wie sollte ich meinen Code so auszugestalten, dass es erfolgreich die STL-Karte mit jedem neuen Updates und LÖSCHEN verwenden?

Nicht leicht. Die Methoden von std::map<Key,T,Compare,Allocator> reservieren und entziehen dynamischen Speicher, , um Elemente der Karte zu speichern und zu verwerfen.

Die Zuordnung und Freigabe von dynamischem Speicher erfolgt über die entsprechenden Elementfunktionen von Allocator. Standardmäßig Allocator ist std::allocator<std::pair<const Key, T>>std::allocator zuordnet, indem sie die globalen operator new und Speicherfreigabe Aufruf durch die globale operator delete Aufruf (das heißt, die Operatoren, die Sie mit Ihrer überwachten Versionen ersetzen, welche Einfügen und Löschen Elemente Ihrer std::map).

Folglich wird, wenn Ihre operator new zum Beispiel unten wird in die Karte das erste Element eingesetzt wird, wird es wieder in dem Einführungsvorgang genannt, die Betrieb eines weiteres Einsetzen beginnt, und so weiter, bis Sie über genügend Arbeitsspeicher ausgeführt und crash (wie du gesehen hast).

Der einzige Weg, dies zu vermeiden, ist Ihre Karte ein std::map<Key,T,Compare,CustomAllocator>, machen wo CustomAllocator ein Allocator ist selbst verfassten dass zuordnet und dynamische Speicher freigibt ::operator new oder ::operator delete ohne Aufruf.

Sie könnten dies versuchen, indem Sie malloc und free der Standard-C-Bibliothek verwenden.

Aber ich würde Sie drängen, nicht zu stören. Wenn Sie annehmen, dass Ihr Speichermanager alles über den Heap enthüllen würde, ist die Art und Weise, in der Sie es schreiben müssen, genug , um Ihnen zu zeigen, dass es nicht geht.Es wird nichts über seine eigene Heap-Nutzung wissen, noch über die Heap-Nutzung von allem anderen in dem Programm, das Heap durch die malloc/free Schnittstelle behandelt verknüpft. Um zu sehen, was mit dem Heap geschieht, ist die Lösung Valgrind Für OS X finden Sie Yosemite and Valgrind

+0

Danke für den Vorschlag, Mike !! Dieses Programm ist für eine Hausaufgabe - mein Professor hat angedeutet, in Beispielcode() im obigen Code zu suchen (ich habe es gerade zu meiner Frage hinzugefügt), aber ich bin unsicher, was er genau sagt. Ich werde versuchen, einen CustomAllocator zu schreiben und zu sehen, ob das funktioniert. –