//  File cow.h
//////////////////////////////////////////////////////
//  Header for Copy-On-Write base classes

#ifndef COW_H             // prevent multiple includes
#define COW_H

//  Obj_COW - a real object (for use by virtual objects)
class Obj_COW {
    //  Allow virtual object access to real obj
    friend class Obj_Virt;
protected:
    //  Constructors and destructor
    Obj_COW(void) { count = 0; }
    //  Functions
    virtual Obj_COW *dup(void) = 0;
private:
    //  Data
    short   count;          //  Count of uses
};

//  Obj_Virt - a virtual object (points to the real object)
class Obj_Virt {
protected:
    //  Constructors and destructor
    Obj_Virt(void) { po = NULL; }
    Obj_Virt(const Obj_Virt& vo) { set_ptr(vo.po); }
    ~Obj_Virt(void) { release(); }
    //  Functions
    Obj_Virt&   operator = (Obj_Virt& vo)
                  { set_ptr(vo.po); return(*this); }
    Obj_COW     *get_ptr(void) { return(po); }
    void        print(void);
    void        release(void);
    void        set_ptr(Obj_COW *pobj);
    void        split(void);
private:
    //  Data
    Obj_COW *po;            //  Pointer to real object
};

//  Obj_Virt::print - print current values
void Obj_Virt::print(void)
{
    printf("po = %p, count = %d\n", po, po->count);
}

//  Obj_Virt::release - release the real object
//                      for this virtual object
inline void Obj_Virt::release(void)
{
    if (NULL != po) {

        //  Decrement count; delete object if zero
        if (0 == --po->count) {
            delete po;
        }
        po = NULL;
    }
}

//  Obj_Virt::set_ptr - do a virtual copy of a real object
inline void Obj_Virt::set_ptr(Obj_COW *pobj)
{
    release();      //  Release current object, if any
    po = pobj;
    po->count++;
}

//  Obj_Virt::split - create a new real object if needed
inline void Obj_Virt::split(void)
{
    if (1 < po->count) {    // Only split if count > 1
        po->count--;
        po = po->dup();
        po->count = 1;
    }
}

#endif  /* COW_H */
