2012-07-07 17 views
10

यह प्रश्न एक std :: मानचित्र में सम्मिलन के दौरान कस्टम आवंटक के उदाहरणों के निर्माण के बारे में है।एसटीएल मानचित्र के लिए कस्टम मेमोरी आवंटक

यहाँ एक छोटा प्रोग्राम यह का उपयोग करता है के साथ std::map<int,int> के लिए एक कस्टम संभाजक है:

#include <stddef.h> 
#include <stdio.h> 
#include <map> 
#include <typeinfo> 

class MyPool { 
public: 
    void * GetNext() { 
    return malloc(24); 
    } 
    void Free(void *ptr) { 
    free(ptr); 
    } 
}; 

template<typename T> 
class MyPoolAlloc { 
public: 
    static MyPool *pMyPool; 

    typedef size_t  size_type; 
    typedef ptrdiff_t difference_type; 
    typedef T*   pointer; 
    typedef const T* const_pointer; 
    typedef T&   reference; 
    typedef const T& const_reference; 
    typedef T   value_type; 

    template<typename X> 
    struct rebind 
    { typedef MyPoolAlloc<X> other; }; 

    MyPoolAlloc() throw() { 
    printf("-------Alloc--CONSTRUCTOR--------%08x %32s\n", this, typeid(T).name()); 
    } 

    MyPoolAlloc(const MyPoolAlloc&) throw() { 
    printf(" Copy Constructor ---------------%08x %32s\n", this, typeid(T).name()); 
    } 

    template<typename X> 
    MyPoolAlloc(const MyPoolAlloc<X>&) throw() { 
    printf(" Construct T Alloc from X Alloc--%08x %32s %32s\n", this, typeid(T).name(), typeid(X).name()); 
    } 

    ~MyPoolAlloc() throw() { 
    printf(" Destructor ---------------------%08x %32s\n", this, typeid(T).name()); 
    }; 

    pointer address(reference __x) const { return &__x; } 

    const_pointer address(const_reference __x) const { return &__x; } 

    pointer allocate(size_type __n, const void * hint = 0) { 
    if (__n != 1) 
     perror("MyPoolAlloc::allocate: __n is not 1.\n"); 
    if (NULL == pMyPool) { 
     pMyPool = new MyPool(); 
     printf("======>Creating a new pool object.\n"); 
    } 
    return reinterpret_cast<T*>(pMyPool->GetNext()); 
    } 

    //__p is not permitted to be a null pointer 
    void deallocate(pointer __p, size_type __n) { 
    pMyPool->Free(reinterpret_cast<void *>(__p)); 
    } 

    size_type max_size() const throw() { 
    return size_t(-1)/sizeof(T); 
    } 

    void construct(pointer __p, const T& __val) { 
    printf("+++++++ %08x %s.\n", __p, typeid(T).name()); 
    ::new(__p) T(__val); 
    } 

    void destroy(pointer __p) { 
    printf("-+-+-+- %08x.\n", __p); 
    __p->~T(); 
    } 
}; 

template<typename T> 
inline bool operator==(const MyPoolAlloc<T>&, const MyPoolAlloc<T>&) { 
    return true; 
} 

template<typename T> 
inline bool operator!=(const MyPoolAlloc<T>&, const MyPoolAlloc<T>&) { 
    return false; 
} 

template<typename T> 
MyPool* MyPoolAlloc<T>::pMyPool = NULL; 

int main(int argc, char *argv[]) { 

    std::map<int, int, std::less<int>, MyPoolAlloc<std::pair<const int,int> > > m; 
    //random insertions in the map 
    m.insert(std::pair<int,int>(1,2)); 
    m[5] = 7; 
    m[8] = 11; 
    printf("======>End of map insertions.\n"); 
    return 0; 
} 

यहाँ इस कार्यक्रम का उत्पादन होता है:

 
-------Alloc--CONSTRUCTOR--------bffcdaa6      St4pairIKiiE 
Construct T Alloc from X Alloc--bffcda77 St13_Rb_tree_nodeISt4pairIKiiEE      St4pairIKiiE 
Copy Constructor ---------------bffcdad8 St13_Rb_tree_nodeISt4pairIKiiEE 
Destructor ---------------------bffcda77 St13_Rb_tree_nodeISt4pairIKiiEE 
Destructor ---------------------bffcdaa6      St4pairIKiiE 
======>Creating a new pool object. 
Construct T Alloc from X Alloc--bffcd9df      St4pairIKiiE St13_Rb_tree_nodeISt4pairIKiiEE 
+++++++ 0985d028 St4pairIKiiE. 
Destructor ---------------------bffcd9df      St4pairIKiiE 
Construct T Alloc from X Alloc--bffcd95f      St4pairIKiiE St13_Rb_tree_nodeISt4pairIKiiEE 
+++++++ 0985d048 St4pairIKiiE. 
Destructor ---------------------bffcd95f      St4pairIKiiE 
Construct T Alloc from X Alloc--bffcd95f      St4pairIKiiE St13_Rb_tree_nodeISt4pairIKiiEE 
+++++++ 0985d068 St4pairIKiiE. 
Destructor ---------------------bffcd95f      St4pairIKiiE 
======>End of map insertions. 
Construct T Alloc from X Alloc--bffcda23      St4pairIKiiE St13_Rb_tree_nodeISt4pairIKiiEE 
-+-+-+- 0985d068. 
Destructor ---------------------bffcda23      St4pairIKiiE 
Construct T Alloc from X Alloc--bffcda43      St4pairIKiiE St13_Rb_tree_nodeISt4pairIKiiEE 
-+-+-+- 0985d048. 
Destructor ---------------------bffcda43      St4pairIKiiE 
Construct T Alloc from X Alloc--bffcda43      St4pairIKiiE St13_Rb_tree_nodeISt4pairIKiiEE 
-+-+-+- 0985d028. 
Destructor ---------------------bffcda43      St4pairIKiiE 
Destructor ---------------------bffcdad8 St13_Rb_tree_nodeISt4pairIKiiEE 

अंतिम दो उत्पादन शो के स्तंभों को std::pair<const int, int> के लिए एक आवंटक हर बार नक्शा में सम्मिलन होता है। यह जरूरी क्यों है? क्या इसे दबाने का कोई तरीका है?

धन्यवाद!

संपादित करें: यह कोड x86 मशीन पर g ++ संस्करण 4.1.2 के साथ परीक्षण किया गया है। यदि आप इसे 64-बिट मशीन पर चलाने की इच्छा रखते हैं, तो आपको कम से कम return malloc(24) लाइन बदलनी होगी। return malloc(48) पर बदलना चाहिए।

उत्तर

1

ऐसा इसलिए है क्योंकि आवंटक std::pair<const int, int> के लिए है, लेकिन कार्यान्वयन को वास्तव में एक और जटिल डेटा संरचना आवंटित करने की आवश्यकता है, जिसमें से एक सदस्य है। जबकि मैं उम्मीद करता हूं कि वास्तविक आवंटक को निर्मित और कैश किया जाना चाहिए, हर बार फिर से निर्माण करना गैरकानूनी नहीं है। यह एक कार्यान्वयन विवरण है जिसे आप अपना कार्यान्वयन बदलने के बिना बच नहीं सकते हैं। बनाया गया वास्तविक आवंटक प्रकार St13_Rb_tree_nodeISt4pairIKiiEE (उलझन वाला नाम) है।

+0

उपरोक्त आउटपुट शो के रूप में हर बार इसे बनाने के लिए स्पष्ट रूप से अवैध नहीं है। क्या कोई अंतर्निहित कारण है कि एसटीएल/सी ++ इसे इस तरह से करना चाहिए? यदि नहीं, तो आप इसे दबाने के लिए कार्यान्वयन को कैसे बदलते हैं? –

+2

सी ++ 03 में, एक आवंटन को आवंटित करने की अनुमति देने की अनुमति दी गई है (खाली)। इस तरह, एक प्रतिलिपि बनाना लगभग मुफ्त है। आपके मामले में, आप संभवतया कॉपी कन्स्ट्रक्टर और असाइनमेंट ऑपरेटर को आंतरिक सूचक की प्रतिलिपि बना सकते हैं, और हर बार एक नया पूल नहीं बना सकते हैं। –

+0

@ बो - प्रश्न का विषय प्रतिलिपि बनाने वाला नहीं है! इसके बजाए यह कन्स्ट्रक्टर है जो St13pR_tree_nodeISt4pairIKiiEE के आवंटक से St4pairIKiiE के लिए आवंटक बनाता है। –

1

MyPool.h (सिंगलटन) में:

class MyPool 
{ 
... 
public: 
    static MyPool & GetInstance(void); 
private: 
    MyPool(void); 
} 

MyPool.cpp में:

MyPool & MyPool::GetInstance(void) 
{ 
    static MyPool retval; 
    return retval; 
} 

fooStdAllocator.h में:

#pragma once 

#include "MyPool.h" 

#pragma push_macro("new") 
#undef new 
#include <new> 

template <class T1> class fooStdAllocator; 

// Description: 
// Specialize for void 
template <> class fooStdAllocator<void> 
{ 
public: 
    typedef void * pointer; 
    typedef const void* const_pointer; 
    typedef void value_type; 
    template <class U1> struct rebind { typedef fooStdAllocator<U1> other; }; 
}; 

template <class T1> class fooStdAllocator 
{ 
public: 
    // Description: 
    // Typedefs 
    typedef T1 value_type; 
    typedef size_t size_type; 
    typedef ptrdiff_t difference_type; 
    typedef T1* pointer; 
    typedef const T1* const_pointer; 
    typedef T1& reference; 
    typedef const T1& const_reference; 

    // Description: 
    // The rebind member allows a container to construct an allocator for some arbitrary type out of 
    // the allocator type provided as a template parameter. 
    template <class U1> struct rebind { typedef fooStdAllocator<U1> other; }; 

    // Description: 
    // Constructors 
    fooStdAllocator(void) : pool(MyPool::GetInstance()) {}; 
    fooStdAllocator(const fooStdAllocator& other) : pool(MyPool::GetInstance()) {}; 
    template <class U1> fooStdAllocator(const fooStdAllocator<U1>&) : pool(MyPool::GetInstance()) {}; 

    // Description: 
    // Destructor 
    ~fooStdAllocator(void) {}; 

    // Description: 
    // Returns the address of r as a pointer type. This function and the following function are used 
    // to convert references to pointers. 
    pointer address(reference r) const { return &r; }; 
    const_pointer address(const_reference r) const { return &r; }; 

    // Description: 
    // Allocate storage for n values of T1. 
    pointer allocate(size_type n, fooStdAllocator<void>::const_pointer hint = 0) 
    { 
    // I would never do it that way: 
    //pointer return_value = reinterpret_cast<pointer>(pool.GetNext()); 
    // I would prefer to use the got size to allocate: 
    pointer return_value = reinterpret_cast<pointer>(pool.GetNext(n)); 

    if (return_value == 0) 
     throw std::bad_alloc(); 
    return return_value; 
    }; 

    // Description: 
    // Deallocate storage obtained by a call to allocate. 
    void deallocate(pointer p, size_type n) 
    { 
    pool.Free(p); 
    }; 

    // Description: 
    // Return the largest possible storage available through a call to allocate. 
    size_type max_size() const 
    { 
    size_type return_value = 0xFFFFFFFF; 
    return_value /= sizeof(T1); 
    return return_value; 
    }; 

    // Description: 
    // Construct an object of type T1 at the location of ptr 
    void construct(pointer ptr) 
    { 
    ::new (reinterpret_cast<void*>(ptr)) T1; 
    }; 

    // Description: 
    // Construct an object of type T1 at the location of ptr, using the value of U1 in the call to the 
    // constructor for T1. 
    template <class U1> void construct(pointer ptr, const U1& val) 
    { 
    ::new (reinterpret_cast<void*>(ptr)) T1(val); 
    }; 

    // Description: 
    // Construct an object of type T1 at the location of ptr, using the value of T1 in the call to the 
    // constructor for T1. 
    void construct(pointer ptr, const T1& val) 
    { 
    ::new (reinterpret_cast<void*>(ptr)) T1(val); 
    }; 

    // Description: 
    // Call the destructor on the value pointed to by p 
    void destroy(pointer p) 
    { 
    p->T1::~T1(); 
    }; 
private: 
    MyPool &pool; 
}; 

// Return true if allocators b and a can be safely interchanged. "Safely interchanged" means that b could be 
// used to deallocate storage obtained through a and vice versa. 
template <class T1, class T2> bool operator == (const fooStdAllocator<T1>& a, const fooStdAllocator<T2>& b) 
{ 
    return true; 
}; 
// Return false if allocators b and a can be safely interchanged. "Safely interchanged" means that b could be 
// used to deallocate storage obtained through a and vice versa. 
template <class T1, class T2> bool operator != (const fooStdAllocator<T1>& a, const fooStdAllocator<T2>& b) 
{ 
    return false; 
}; 
#pragma pop_macro("new") 

आप इस प्रकार के रूप में उपयोग कर सकते हैं:

std::map<keyT,valueT,std::less<keyT>,fooStdAllocator> your_map; 
+0

मुझे लगता है कि 3 निर्माण कार्यों में से 2 को reinterpret_cast <> की आवश्यकता नहीं है। क्या आप समझा सकते हैं कि इस कोड को क्यों काम करना चाहिए? (मुझे सोमवार तक मशीन तक पहुंच नहीं होगी।) –

+0

@ प्र्रासून तिवारी: [इस विषय पर वास्तव में अच्छा लेख।] (Http://www.codeguru.com/cpp/cpp/cpp_mfc/stl/article.php/c4079) – Naszta

+0

@ प्र्रासून तिवारी: 'reinterpret_cast's: सच हो सकता है ('नए' ऑपरेटरों के लिए हो सकता है), लेकिन मैंने आपके लिए एक कामकाजी और परीक्षण कोड कॉपी किया। :) – Naszta

संबंधित मुद्दे