Thread: overloading new/delete, memory pooling, singleton destructors, etc.

  1. #1
    Registered User
    Join Date
    Aug 2002
    Location
    Hermosa Beach, CA
    Posts
    446

    overloading new/delete, memory pooling, singleton destructors, etc.

    Ok, so I was bored today and decided to write a simple memory pooling demo, where I have a singleton memory_pool object, and then the clients of the memory pool make use of it by overloading new and delete. The memory_pool exists on a per type basis, which is done with templates.

    So first I have a general question about singletons, and second I was looking for general feedback, in terms of if my memory_pool object is implemented incorrectly, could be doing something better, etc. Also I've never actually overloaded new/delete before, so I was wondering if there were special issues I should be aware of.

    Question 1: How do I get the singleton destructor to fire? As far as I can tell, it never will fire, because I would need to explicitly call delete against my instance_ pointer. Is there a standard way to do this?

    Question 2: Is there any improvements I can make, or did I implement anything incorrectly? There are two shortcomings that I can see right away: a) Not thread-safe (easy to fix); b) new[] and delete[] are not handled by the memory_pool, so global new[] and delete[] would be used (not so easy to fix, the implementation would probably have to be completely different).

    Ok, here's the code:

    Code:
    # include <new>
    # include <list>
    # include <algorithm>
    # include <iostream>
    using namespace std;
    
    template <class T, int N = 100>
    class memory_pool {
        class delete_it {
            public:
            void operator()(char* p) {
                cout << "Deleting memory at " << p << endl;
                delete[] p;
            }
        };
    
        memory_pool() {
            // preallocate N objects
            alloc_n();
        }
    
        void alloc_n() {
            for (int i = 0; i < N; i++) {
                char* p = new char[sizeof(T)];
    
                cout << "Allocating memory at " << reinterpret_cast<T *>(p) << endl;
    
                pool_.push_back(p);
            }
        }
    
        public:
    
        virtual ~memory_pool() {
            // destroy each object still inuse
            while (!inuse_.empty()) {
                char* p = inuse_.front();
                Destroy(p);
            }
    
            // delete all pooled objects
            for_each(pool_.begin(), pool_.end(), delete_it());
        }
    
        static memory_pool<T,N>* Instance() {
           if (!memory_pool<T,N>::instance_) memory_pool<T,N>::instance_ = new memory_pool<T,N>();
    
           return memory_pool<T,N>::instance_;
        }
    
        T* Create() {
            if (pool_.empty()) {
                // allocate N more objects
                alloc_n();
            }
    
            // remove element from available pool
            char* p = pool_.front();
            pool_.pop_front();
    
            // put onto inuse list
            inuse_.push_back(p);
    
            return reinterpret_cast<T*>(p);
        }
    
        private:
        void Destroy(char* p) {
            // remove element from inuse pool
            inuse_.remove(p);
    
            // add back to available pool
            pool_.push_back(p);
        }
        public:
        void Destroy(T* p) {
            Destroy(reinterpret_cast<char *>(p));
        }
    
        static memory_pool<T,N>* instance_;
    
        private:
        list<char*> pool_;
        list<char*> inuse_;
    };
    
    class Demo {
        int x;
    
        public:
        Demo() {
            x = 0;
            cout << "Constructing Demo " << this << endl;
        }
    
        ~Demo() {
            cout << "Destructing Demo " << this << endl;
        }
    
        void* operator new(size_t sz);
    
        void operator delete(void* p, size_t sz);
    };
    
    typedef memory_pool<Demo,5> DemoPool;
    DemoPool* DemoPool::instance_ = 0;
    
    void* Demo::operator new(size_t sz) {
       return DemoPool::Instance()->Create();
    }
    
    void Demo::operator delete(void* p, size_t sz) {
        DemoPool::Instance()->Destroy(static_cast<Demo *>(p));
    }
    
    int main() {
    
        for (int i = 0; i < 10; i++) {
            Demo* d = new Demo();
            delete d;
        }
    
        return 0;
    }
    The crows maintain that a single crow could destroy the heavens. Doubtless this is so. But it proves nothing against the heavens, for the heavens signify simply: the impossibility of crows.

  2. #2
    Registered User
    Join Date
    Mar 2006
    Posts
    725
    * 0_o *
    * 5 seconds to get over code shock *
    * ... *
    * ... *
    * okay, I'm fine *


    g++ 3.4.2:
    Code:
    106: error: too few template-parameter-lists
    106: error: expected `,' or `;' before '=' token
    108: warning: unused parameter 'sz'
    112: warning: unused parameter 'sz'
    Code:
    #include <stdio.h>
    
    void J(char*a){int f,i=0,c='1';for(;a[i]!='0';++i)if(i==81){
    puts(a);return;}for(;c<='9';++c){for(f=0;f<9;++f)if(a[i-i%27+i%9
    /3*3+f/3*9+f%3]==c||a[i%9+f*9]==c||a[i-i%9+f]==c)goto e;a[i]=c;J(a);a[i]
    ='0';e:;}}int main(int c,char**v){int t=0;if(c>1){for(;v[1][
    t];++t);if(t==81){J(v[1]);return 0;}}puts("sudoku [0-9]{81}");return 1;}

  3. #3
    Registered User
    Join Date
    Aug 2002
    Location
    Hermosa Beach, CA
    Posts
    446
    Ummm...Well I'm using g++ 3.3.2 and I get no warnings or errors. Your first error I think points to:

    Code:
    typedef memory_pool<Demo,5> DemoPool;
    DemoPool* DemoPool::instance_ = 0; // error here
    Not sure what the hell g++ is complaining about, since memory_pool takes 2 template parameters, and clearly I'm giving it both of 'em.

    Anyway. It would be nice to know about how to get a singleton's destructor to run...maybe I'm supposed to use atexit or something like that?
    The crows maintain that a single crow could destroy the heavens. Doubtless this is so. But it proves nothing against the heavens, for the heavens signify simply: the impossibility of crows.

  4. #4
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    If you don't get enough replies here, consider codeguru's non-Visual C++ forum. More advanced topics tend to get more responses there.

  5. #5
    carry on JaWiB's Avatar
    Join Date
    Feb 2003
    Location
    Seattle, WA
    Posts
    1,972
    From what I gather, if you don't need control over when the instance is destroyed, and are in a single-threaded environment, you can use a static instance, without any dynamic allocation ("Meyers" implementation?)

    I've also seen singleton "destroyer" classes, which, it seems, can be used in a multi-threaded environment, but don't allow control over when the instance is destroyed. (Something like the last example here)

    A good book on design patterns would probably answer your questions a lot better
    "Think not but that I know these things; or think
    I know them not: not therefore am I short
    Of knowing what I ought."
    -John Milton, Paradise Regained (1671)

    "Work hard and it might happen."
    -XSquared

  6. #6
    Registered User
    Join Date
    Aug 2002
    Location
    Hermosa Beach, CA
    Posts
    446
    ahh yes, that is what I was looking for. Thanks.
    The crows maintain that a single crow could destroy the heavens. Doubtless this is so. But it proves nothing against the heavens, for the heavens signify simply: the impossibility of crows.

  7. #7
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    1) The standard way of getting a destructor of a singleton to fire is simple: destroy the singleton. Assuming you want the singleton to be created using operator new (as opposed to being a static) There are three obvious alternatives;

    a) Provide a non-static member function that calls "delete this;".

    b) Provide a static member function that deletes the instance.

    c) Rather than having instance being a static raw pointer, make it into a std::auto_ptr<memory_pool<T,N> >.

    The catch with the first two approaches is that they rely on some code, somewhere, somehow remembering to destroy the instance. The catch with the third approach is that it relies on the program exiting normally (eg via a return from main()) and abnormal exits (eg calls to abort()) will not destroy the object. It also requires some care to ensure that the Instance() function does not make that auto_ptr lose ownership, so some other smart pointer (eg one of the smart pointers from the boost library, or one you roll yourself) might be a better option.

    2) I would query whether it is easy to make the code thread safe, but I'll leave that alone (apart from noting that ensuring creation of the singleton is thread safe would be easier if you create the singleton before creating any threads --- and ensuring that comes down to design of the program using your objects, rather than design of your objects). It is a fallacy than any object can protect itself from multithreaded access: the best you can do is ensure that code which uses objects also protects those objects from multithreaded access --- which again comes down to design of the program rather than the objects.

    3) operator new() and operator delete() are not handled by the memory_pool either. They are members of your Demo class. You could provide versions of operator new []() and operator delete [] in your Demo class though.
    Last edited by grumpy; 09-14-2006 at 10:48 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Memory handler
    By Dr. Bebop in forum C Programming
    Replies: 7
    Last Post: 09-15-2002, 04:14 PM
  2. Inheritance, Destructors, and Memory Leaks
    By Aran in forum C++ Programming
    Replies: 0
    Last Post: 09-02-2002, 09:02 PM
  3. Is it necessary to write a specific memory manager ?
    By Morglum in forum Game Programming
    Replies: 18
    Last Post: 07-01-2002, 01:41 PM
  4. Replies: 3
    Last Post: 01-22-2002, 12:25 PM
  5. What's the best memory (RAM) type?
    By Unregistered in forum A Brief History of Cprogramming.com
    Replies: 17
    Last Post: 12-15-2001, 12:37 AM