FBB::RefCount

FBB::RefCount

libbobcat1-dev_2.02.03-x.tar.gz

2005-2009


FBB::RefCount(3bobcat)

FBB::RefCount(3bobcat)

libbobcat1-dev_2.02.03-x.tar.gz Reference Counting

2005-2009

NAME

FBB::RefCount - Base class implementing reference counting.

SYNOPSIS

#include <bobcat/refcount>
Linking option: -lbobcat

DESCRIPTION

RefCount implements a virtual base class implementing reference counting. When reference counting is used, objects share the memory of a (usually big or complex) data structure, until objects need to modify their data, in which case they obtain a copy of the data of their own. This approach is most useful with classes that seldomly alter their data, but consult their data most of the time.

As an example, consider hidden structures as found in the regcomp(3) function: there is no documented way to copy an existing compiled regular expression, so if multiple objects must refer to the same regular expression, the expression should be compiled once. Thereafter, a reference count monitors the number of objects using the compiled expression. Only when the last object is destroyed the compiled expression is freed.

In general, objects using reference counting should obtain their own data if they need to alter their data. This situation is also called `copy-on-write'. Copy-on-write is implemented by obtaining a copy of the data before any modification of the data takes place. So, each non-const member function should first copy the data, and should only then modify its own data. Constant members may simply refer to the data, without the need to copy them first.

The class RefCount should be embedded in programs as follows:

Except for clone(), there are several issues to bear in mind when using reference counting as defined by RefCount:

Except for the abovementioned items, all members of Client should be implemented as usual: constructors use new Data(argument list), clone() returns a pointer to a clone of itself, etc.. Refer to the code example for an actual implementation.

NAMESPACE

FBB
All constructors, members, operators and manipulators, mentioned in this man-page, are defined in the namespace FBB.

INHERITS FROM

-

PROTECTED CONSTRUCTORS

PROTECTED DESTRUCTOR

PUBLIC MEMBER FUNCTIONS

PUBLIC STATIC MEMBER FUNCTIONS

EXAMPLE

The following example illustrates the use the class RefCount. A class Data is derived from RefCount, defining clone(), several standard members (copy constructor, overloaded assignment operator) as private members, and a default constructor, destructor, accessor and modifier member as public members.

The class that is used directly by the program is Client, given next. It defines all standard constructors and members, and it shadows the accessor and modifier members of Data:

Finally, a small program using Client is shown.

/*
                              driver.cc
*/

#include <iostream>
#include <string>
#include <sstream>

#include "../refcount"

using namespace std;
using namespace FBB;

// The class Data uses reference counting. Data are shared until they are
// modified. 

class Data: public RefCount
{
    static size_t s_n;        // count the number of objects in use

    public:
        Data()                  // all other constructors are built like this:
        {                       // using RefCount's default constructor.
            s_n++;
            cout << "Data(), " << s_n << " Data objects created\n";
        }

        virtual ~Data()
        {
            s_n--;
            cout << "~Data(), " << s_n << " Data objects left\n";
        }

        string accessor() const
        {
            ostringstream ostr;
            ostr << "calling Data::accessor(). Data at " << this;
            return ostr.str();
        }

        void modifier()             // a plain modifier
        {
            cout << "calling Data::modifier(). Data at " << this << endl;
        }

                                    // support function for operator>>()
        istream &extract(istream &istr) 
        {
            cout << "extraction from istream. " <<
                                    "Enter a non-empty line of text" << endl;
            string s;
            getline(istr, s);

            cout << "Read: " << s << endl;

            return istr;
        }
                                    // another modifier: operator+=()
        Data &operator+=(Data const &rvalue)
        {
            cout << "calling Data::operator+=(): @`" << 
                    this << "' += @`" << &rvalue << "'\n";
            return *this;
        }

    private:
        Data &operator=(Data const &other);   // NI

        Data(Data const &other) // The copy constructor MUST call RefCount's
        :                       // CC. Data(Data) is a convenience function 
                                // for clone() and should not be available to 
            RefCount(other)     // clients, thus enforcing the use of 
        {                           // clone() / share() / release()
            s_n++;
            cout << "Data(Data const &), " << s_n << " Data objects created\n";
        }

        virtual RefCount *clone() const
        {
            cout << "Data::clone()\n";
            return new Data(*this);
        }
};


// Client: uses a pointer to a Data object. It is implemented in an 
// almost standard way. Deviations are:
//  *. Copy():       should call share() rather than new Data(*d_dataPtr)
//  *. Destroy():    should call release() rather than delete d_dataPtr;
//  *. non-const members modifying d_dataPtr's data"    
//                   should call Data:modifying() first.
class Client 
{
                                                     // modifying friend
                                                     // see below at modifier()
    friend istream &operator>>(istream &istr, Client &c)
    {   
        return Data::modifying(&c.d_dataPtr).extract(istr);
    }

    Data *d_dataPtr;   

    public:
            // Constructors, destructor, overloaded assignment operator: all
            // follow my standard copy() / destroy() approach. 

        Client()                    // new object, creates its own data
        :                           // use new Data(...) to initialize.
            d_dataPtr(new Data())
        {}

        ~Client()
        {
            destroy();
        }

        Client(Client const &other)
        {
            copy(other);
        }

        Client &operator=(Client const &other)
        {
            if (this != &other)
            {
                destroy();
                copy(other);
            }
            return *this;
        }

        string accessor() const         // accessors shadow Data's members
        {                               
            return d_dataPtr->accessor();
        }
                                        // modifiers call modifying first

        void modifier()                 // simple modifier
        {                               
            Data::modifying(&d_dataPtr).modifier();
        }
                                        // arithmetic assignment modifier
        Client &operator+=(Client const &other)
        {                               
            Data::modifying(&d_dataPtr).operator+=(*other.d_dataPtr);
            return *this;
        }

    private:
        void copy(Client const &other)  // copying is sharing: call share()
        {
            d_dataPtr = Data::share(other.d_dataPtr);
        }
        void destroy()                  // destroying is disassociation: call
        {                               // release
            d_dataPtr->release();
        }
};


size_t Data::s_n = 0;

Client const operator+(Client const &lvalue, Client const &rvalue)
{
    return Client(lvalue) += rvalue;
}

int main()
{
    cout << "Construction:\n";
    Client c;

    cout << "Extraction c from cin:\n";
    cin >> c;

    cout << "c's Modifier called:\n";
    c.modifier();

    cout << "operator += :\n";
    c += c;

    cout << "operator + :\n";
    c + c;

    cout << "Copy construction:\n";
    Client c2(c);

    cout << "Assignment:\n";
    c = c2;

    cout << "Accessors:\n";
    cout << "access c:  " << c.accessor() << endl;
    cout << "access c2: " << c2.accessor() << endl;

    cout << "operator += :\n";
    c += c;

    cout << "c's Modifier called:\n";
    c.modifier();

    cout << "Accessors:\n";
    cout << "access c:  " << c.accessor() << endl;
    cout << "access c2: " << c2.accessor() << endl;

    cout << "c2's Modifier called:\n";
    c2.modifier();

    cout << "resetting refcount to 2:\n";
    c = c2;

    
    cout << "Extraction c from cin:\n";
    cin >> c;

    cout << "End of program:\n";
}

FILES

bobcat/refcount - defines the class interface

SEE ALSO

bobcat(7)

BUGS

None Reported.

DISTRIBUTION FILES

BOBCAT

Bobcat is an acronym of `Brokken's Own Base Classes And Templates'.

COPYRIGHT

This is free software, distributed under the terms of the GNU General Public License (GPL).

AUTHOR

Frank B. Brokken (f.b.brokken@rug.nl).