McLovin
McLovin

Reputation: 3417

A shared resource for an object

Say I have this class called Dog. Every dog has a different name but the same barking voice (which is loaded from a resource file).

class Dog {
public:
    Dog(const string &name) : _name(name) {
        _barkingVoice.load();
    }
    ~Dog() {
        _barkingVoice.free();
    }

    string getName() const { return _name; }
    void bark() { _barkingVoice.play(); }

private:
    string _name;
    VoiceResource _barkingVoice;
};

I want to call _barkingVoice.load() only if the instance of Dog is the first one, and _barkingVoice.free() only if there are no more instances of Dog.

The obvious solution is to set _barkingVoice as static and keep a reference counter of Dog as a data member.

My question is if there's an easier way to do this. Maybe an std implementation or something like that.

Upvotes: 3

Views: 434

Answers (3)

Emil Laine
Emil Laine

Reputation: 42838

Make a reusable class to encapsulate the reference counting:

template<class ResourceType, class OwnerType>
class RefCounted {
public:
    RefCounted()          { if (++_refCount == 1) _resource.load(); }
    virtual ~RefCounted() { if (--_refCount == 0) _resource.free(); }
    ResourceType& operator*()  { return  _resource; }
    ResourceType* operator->() { return &_resource; }
private:
    static unsigned _refCount;
    static ResourceType _resource;
};

template<class T, class U> unsigned RefCounted<T, U>::_refCount = 0;
template<class T, class U> T RefCounted<T, U>::_resource;

class Dog {
public:
    Dog(const string &name) : _name(name) { }

    string getName() const { return _name; }
    void bark() { _barkingVoice->play(); }

private:
    string _name;
    RefCounted<VoiceResource, Dog> _barkingVoice;
};

Every template instantiation will have their own _refCount and _resource.

The second template parameter is to handle cases where you instantiate RefCounted with the same ResourceType but want to have separate reference counting for those instantiations. E.g. if you add a Cat class and want it to have its own Refcounted<VoiceResource>:

class Cat {
    // ...
private:
    RefCounted<VoiceResource, Cat> _meowingVoice;
};

Upvotes: 2

Lapshin Dmitry
Lapshin Dmitry

Reputation: 1124

First, why VoiceResource is not static? If it's shared between all instances of Dog, it should be. Else, you will need to load or copy the resuorce at every constructor call.

Have a static variable static int instanceCount;, that is set to 0. In every casual, copy and move (C++11) constructor increment it, in destructor decrement it. That will give you opportunity to do what you wanted.

That will basicly work like shared_ptr<T> does, the might be a way to use it here instead of writing your own code, I just can't figure that out.

class Dog {
public:
    Dog(const string &name) : _name(name) {
        loadResource();
    }
    Dog(const Dog& b) : name(b.name) {            
        loadResource();
    }
    // only C++11:
    Dog(Dog&& b) : name(std::move(b.name))  {
        loadResource();
    }

    ~Dog() {
        freeResource();
        _barkingVoice.free();
    }

    string getName() const { return _name; }
    void bark() { _barkingVoice.play(); }

private:
    string _name;
    static VoiceResource _barkingVoice;
    static int instanceCount;

    static void loadResource() {
        if (instanceCount == 0) {
            _barkingVoice.load();
        }
        ++instanceCount;
    }
    static void freeResource() {
        --instanceCount;
        if (instanceCount == 0) {
            _barkingVoice.free();
        }
    }
};
int Dog::instanceCount = 0;

Upvotes: 2

Emil Laine
Emil Laine

Reputation: 42838

Make _barkingVoice a std::shared_ptr<VoiceResource>.

A shared_ptr does exactly what you need: uses references counting to track the deletion of the last object, when it will deallocate the resource.

Upvotes: 1

Related Questions