SRC: управление коллекциями полиморфных объектов
От: Анатолий СССР  
Дата: 29.08.02 07:12
Оценка:
///////////////////////////////////////////////////////////////////////////////
//
// header: pointer.h
//
// title:  Определение шаблона pointer
//
// date:   09/11/00
//
// author: Широков Анатолий
//
#ifndef pointer_h
#define pointer_h

///////////////////////////////////////////////////////////////////////////////
//    template<class T> class pointer (declaration)
//    
/*+
DESC:

Шаблон pointer предназначен для упрощения управления контейнерами
содержащих элементы полиморфной природы.
Для понимания рассмотрим пример:

    class Shape
    {
    public:
        virtual ~Shape() {}
        virtual void draw(Context &) const = 0; 
        virtual void rotate(double) = 0; 
    }; // class Shape..

    typedef vector<Shape*> Shapes;

    class Drawing
    {
    public:
        Drawing();
        Drawing(const Drawing&);
        Drawing& operator=(const Drawing&);
        ~Drawing();
    private:
        Shapes shapes;
    };

В рамках этого определения (vector<Shape*>)    нам необходимо позаботиться
об удалении объектов ссылки которых хранит Shapes, написать код копирующего
конструктора и копирующего присваивания. Если это единичный случай 
использования данной модели, то это можно стерпеть, но, в конечном итоге,
если в проекте существует несколько иерархий классов, то можно задуматься и
о более элегантном и безопастном способе.

Решение предлагаемое ниже, основано на природе автоматического объекта, который
берет на себя управление над указателем на объект базового класса иерархии 
(копирование, удаление). Шаблон pointer очень похож на решение STL auto_ptr,
за исключением семантики копирования.

Посмотрим как упростится код после применения шаблона:
    typedef vector<pointer<Shape> > Shapes;

    class Drawing
    {
    private:
        Shapes shapes;
    };

Мы теперь довольствуемся кодом сгенерированным компилятором, что видит Бог,
надежней.

HOWTO:
#include <iostream>
#include <vector>

#include "pointer.h"

///////////////////////////////////////////////////////////////////////////////
class Shape
{
public:
    virtual ~Shape()
    {
        std::cout << "Shape destroed\n";
    }
    virtual void draw(void) const = 0; 
    virtual void rotate(double) = 0; 

    virtual Shape* copy(void) const = 0;
}; // class Shape..

typedef std::vector< pointer<Shape> > ShapeList;

class Drawing
{
public:
    ShapeList shapes;
};
///////////////////////////////////////////////////////////////////////////////
class Rectangle : public Shape
{
public:
    void draw(void) const
    {
        std::cout << "Rectangle::draw\n";
    }
    void rotate(double)
    {
        std::cout << "Rectangle::rotate\n";
    }

    Shape* copy(void) const 
    {
        std::cout << "Rectangle::copy\n";
        return new Rectangle;
    }
};

class Circle : public Shape
{
public:
    void draw(void) const
    {
        std::cout << "Circle::draw\n";
    }
    void rotate(double)
    {
        std::cout << "Circle::rotate\n";
    }

    Shape* copy(void) const 
    {
        std::cout << "Circle::copy\n";
        return new Circle;
    }
};

void test(Drawing &drawing)
{
    // константная итерация
    ShapeList::const_iterator ci = drawing.shapes.begin();

    for (;ci!=drawing.shapes.end();++ci)
    {
        (*ci)->draw();
    }

    // модифицирующая итерация
    ShapeList::iterator i = drawing.shapes.begin();
    for (;i!=drawing.shapes.end();++i)
    {
        // осуществляется копирование представления, если оно было разделено с кем-то
        (*i)->rotate(3.14); 
    }
}

void main()
{
    Drawing drawing1;
    
    drawing1.shapes.push_back(new Rectangle);
    drawing1.shapes.push_back(new Circle);
    
    std::cout << "drawing1:\n";
    std::cout << "=========\n";

    test(drawing1);

    std::cout << "=========\n";

    Drawing drawing2 = drawing1;

    drawing2.shapes.push_back(new Rectangle);

    std::cout << "drawing2:\n";
    std::cout << "=========\n";

    test(drawing2);

    std::cout << "=========\n";
}
OUTPUT:
drawing1:
=========
Rectangle::draw
Circle::draw
Rectangle::rotate
Circle::rotate
=========
drawing2:
=========
Rectangle::draw
Circle::draw
Rectangle::draw
Rectangle::copy
Rectangle::rotate
Circle::copy
Circle::rotate
Rectangle::rotate
=========
Shape destroed
Shape destroed
Shape destroed
Shape destroed
Shape destroed

NOTE:
    Требования предъявляемые к классу - параметру шаблона:
    должна быть определена виртуальная функция copy, которая создает новый
    объект класса, инициализируемый состоянием исходного объекта.
-*/

template <class T> class copyclass
{
public:
    static T* copy(const T* originPtr)
    {
        if (originPtr)
            return originPtr->copy();
        return 0;
    }
};

template <class T, class A = copyclass<T> > class pointer
{
public:
    typedef T base_type;
    typedef pointer<T, A> self_type;
public:
    // разделяемое представление указателя на объект класса T
    class rep
    {
    public:
        //    определяем друзей
        friend class pointer<T, A>;
    public:
        // конструктор по умолчанию
        rep(base_type* object=0) : m_object(object), use_count(1) {}
        // деструктор
        ~rep() {if (m_object) delete m_object;}
        // копирование представление
        rep* copy() 
        {
            if (use_count==1) return this;
            --use_count;
            if (m_object)
                return new rep(A::copy(m_object));
            else
                return new rep();
        }
    private:
        //    предотвращаем копирование состояния объекта
        rep(const rep&);
        rep& operator = (const rep&);        
    private:
        int use_count;
        base_type* m_object;
    }; // class rep..
public:
    // конструктор по умолчанию
    pointer(base_type* object=0) : m_rep(new rep(object)) {}
    // деструктор
    ~pointer() 
    { 
        if (--m_rep->use_count==0)
            delete m_rep;
    }
    // конструктор копирования
    pointer(const self_type& ptr)
    {
        ptr.m_rep->use_count++;
        m_rep = ptr.m_rep;
    }
    // оператор присваивания
    self_type& operator = (const self_type& ptr)
    {
        if (this == &ptr) return *this;
        if (--m_rep->use_count==0) delete m_rep;
        ptr.m_rep->use_count++;
        m_rep = ptr.m_rep;
        return *this;
    }
    self_type& operator = (base_type* object)
    {
        assign(object);
        return *this;
    }
    //    функция присваивания
    void assign(base_type *object)
    {
        if (m_rep->m_object==object) return;
        if (--m_rep->use_count==0) delete m_rep;
        m_rep = new rep(object);
    }
    //    селектор объекта 
    base_type* get() {return (m_rep = m_rep->copy())->m_object;}
    const base_type* get_const() {return m_rep->m_object;}
    //    селектор объекта с запретом изменять состояние модификации представления
    const base_type* get() const {return m_rep->m_object;}

    //    Операторы разименования
    //    - модифицирующие представление (отложенное копирование осуществляется здесь)
    base_type& operator*() {return *(get());}
    base_type* operator->() {return get();}
    //    - не модифицирующие представление 
    //    !если будет применен метод грубой силы const_cast, то шаблон не несет 
    //    !никакой ответственности за порчу разделяемого объекта.
    const base_type& operator*() const {return *(get());}
    const base_type* operator->() const {return get();}
private:
    rep* m_rep;
}; // template <class T> class pointer..
#endif pointer_h
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.