lundi 2 novembre 2020

is custom allocator is required to manage all the memory used by std::map?

To manage all the memory allocation done by std::map is it enough to store the map values as a pointer? I thought that std::map might internally allocate tree nodes, for which it may use std::allocator which might have class scope operator new or operator delete (not sure though).

To find this, I did a small test using a custom std conforming allocator that uses overloaded operator new and operator delete. Here is the code:

#include <iostream>
#include <memory>
#include <map>
#include <vector>
#include <algorithm>


int cnt = 0;
int num_elem = 0;

class A {
public:
  int x;
  char buffer[1000];
};




class OverloadNew {
  public:
  
  static void* newImpl(std::size_t sz,char const* file, int line){
      void* ptr= std::malloc(sz);
      cnt++;
      std::cerr << file << ": " << line << " " <<  ptr << std::endl;
      return ptr;
  }
  static void* operator new(std::size_t sz,char const* file, int line){  
      return newImpl(sz,file,line);
  }
  static void* operator new [](std::size_t sz,char const* file, int line){  
      return newImpl(sz,file,line);
  }
  static void operator delete(void* ptr) noexcept{
      std::free(ptr);
  }
};


template <class T>
class my_allocator {
public:
  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;

  my_allocator() {}
  my_allocator(const my_allocator&) {}
  pointer   allocate(size_type n, const void * = 0) {
              T* t = (T*)(OverloadNew::operator new(n*sizeof(T),__FILE__,__LINE__));
              std::cout
              << "Inside Map allocate   at address "
              << t << " (+)" << std::endl;
              return t;
            }
  
  void      deallocate(void* p, size_type) {
              if (p) {
                //free(p);
                OverloadNew::operator delete(p);
                std::cout
                << "Inside Map deallocate at address "
                << p << " (-)" << 
                std::endl;
              } 
            }

  pointer           address(reference x) const { return &x; }
  const_pointer     address(const_reference x) const { return &x; }
  my_allocator<T>&  operator=(const my_allocator&) { return *this; }
  void              construct(pointer p, const T& val) 
                    { new ((T*) p) T(val); }
  void              destroy(pointer p) { p->~T(); }
  size_type         max_size() const { return size_t(-1); }
  template <class U>
  struct rebind { typedef my_allocator<U> other; };
  template <class U>
  my_allocator(const my_allocator<U>&) {}
  template <class U>
  my_allocator& operator=(const my_allocator<U>&) { return *this; }
};




/* Object is a class that has a std::map member */
class Object {
public:

  static void* newImpl(std::size_t sz,char const* file, int line) {
      void* ptr= std::malloc(sz);
      cnt++;
      std::cerr <<"Inside Object class "<<file << ": " << line << " " <<  ptr << std::endl;
      return ptr;
  }

  static void* operator new(std::size_t sz,char const* file, int line){  
      return newImpl(sz,file,line);
  }

  static void* operator new [](std::size_t sz,char const* file, int line){  
      return newImpl(sz,file,line);
  }

  static void operator delete(void* ptr) noexcept{
      std::free(ptr);
  }

  void insertAnyOneElement() {
    
    void* ptr = this->operator new(sizeof(A),__FILE__,__LINE__);
    A* a = new(ptr) A();
    Map.insert({num_elem++,a});

  }

  std::map<int,A*,std::less<int>,my_allocator<std::pair<const int, A*>>> Map;

};






int main() {
  
  Object obj;
  obj.insertAnyOneElement();
  std::cout <<"total allocation :" << cnt << std::endl;


  
}


/*

Inside Object class temp.cpp: 116 0xe47018
temp.cpp: 55 0xe40ed0
Inside Map allocate   at address 0xe40ed0 (+)
total allocation :2
Inside Map deallocate at address 0xe40ed0 (-)

*/

I found that the map internally allocates new memory. I have two questions.

  1. Is there any other way to manage the memory allocation/deallocation done by std::map?
  2. Is there any other memory allocation that can happen inside std::map class,(apart from the stored value), in another way, is the total allocation size of memory inside std::map is greater than the total size of the values being stored?

Thank You!

Aucun commentaire:

Enregistrer un commentaire