/*
(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