1. 4FipS.com
  2. Photos
  3. Videos
  4. Code
  5. Forums
  6. pfQuizzz
  7. About
/*
(c) 2013 +++ Filip Stoklas, aka FipS, http://www.4FipS.com +++
THIS CODE IS FREE - LICENSED UNDER THE MIT LICENSE
ARTICLE URL: http://forums.4fips.com/viewtopic.php?f=3&t=1066
*/

#include <string>
#include <vector>
#include <memory>
#include <algorithm>
#include <iostream>
#include <cassert>

size_t g_num_allocs = 0;

/// Overloaded global new, just to see allocations.
void * operator new (size_t size)
{
    void *ptr = ::malloc(size);
    std::cout << "+ new: 0x" << ptr << ", " << size << " byte(s)\n";
    ++g_num_allocs;
    return ptr;
}

/// Overloaded global delete, just to see deallocations.
void operator delete (void *ptr)
{
    std::cout << "- delete: 0x" << ptr << "\n";
    ::free(ptr);
}

/// A Resource that can by instantiated only through the Manager.
class Resource
{
 public:

    const std::string & name() const { return _name; }

 private:

    friend class Manager; ///< Resource factory.

    Resource();
    Resource(const std::string &name) : _name(name) {}

    std::string _name;
};

typedef std::shared_ptr<Resource> Resource_holder;
typedef std::weak_ptr<Resource> Resource_handle;

/// A helper that asserts on an attempt to lock an expired weak_ptr.
template <typename T>
std::shared_ptr<T> safe_lock(std::weak_ptr<T> w)
{
    std::shared_ptr<T> p(w.lock());
    assert(p && "An expired weak_ptr detected!");
    return p;
}

/// A Manager that explicitly controls the lifetime of Resources.
class Manager
{
 public:

    Manager() {}

    /// Returns a handle (weak pointer) to the newly created Resource.
    Resource_handle create_resource(const std::string &name)
    {
        _resources.emplace_back(Resource_holder(new Resource(name)));
        return _resources.back();
    }

    /// Destroys an existing Resource using the provided handle (weak pointer).
    void destroy_resource(Resource_handle r)
    {
        assert(std::find(begin(_resources), end(_resources), safe_lock(r)) != end(_resources));
        _resources.erase(remove(begin(_resources), end(_resources), safe_lock(r)), end(_resources));
    }

 private:

    Manager(const Manager &); // no copy
    Manager & operator = (const Manager &); // no assignment

    std::vector<Resource_holder> _resources;
};

int main()
{
    //Resource r("A"); // doesn't compile!
    //Resource *r = new Resource("A"); // doesn't compile!

    Manager mgr;

    std::cout << "<X>\n";

    Resource_handle r1 = mgr.create_resource("Resource A");
    std::cout << safe_lock(r1)->name() << "\n";

    std::cout << "<Y>\n";
    Resource_handle r2 = r1;
    std::cout << safe_lock(r2)->name() << "\n";
    std::cout << "<Y>\n";

    mgr.destroy_resource(r1);

    //m.destroy_resource(r2); // asserts! (double destroy)
    //std::cout << safe_lock(r1)->name() << "\n"; // asserts! (already destroyed)

    std::cout << "<X>\n";
    std::cout << "Total number of allocations: " << g_num_allocs << "\n";

    return 0;
}

// Compiled under Visual C++ 2012 (debug), output:
// + new: 0x00816470, 8 byte(s)
// <X>
// + new: 0x0081C3E8, 8 byte(s)
// + new: 0x0081C888, 28 byte(s)
// + new: 0x0081C500, 8 byte(s)
// + new: 0x0081B380, 16 byte(s)
// + new: 0x0081C378, 8 byte(s)
// - delete: 0x0081C3E8
// Resource A
// <Y>
// Resource A
// <Y>
// - delete: 0x0081C500
// - delete: 0x0081C888
// <X>
// Total number of allocations: 6
// - delete: 0x0081B380
// - delete: 0x0081C378
// - delete: 0x00816470