Шаблон проектирования C ++ для использования абстрактного объекта класса в другом классе - PullRequest
1 голос
/ 13 июня 2011

У меня есть класс с двумя чистыми виртуальными методами и другим классом, который должен использовать объект этого класса. Я хочу позволить пользователю этого класса указать, какой вывод абстрактного класса следует использовать внутри него. Я изо всех сил пытаюсь выяснить, каков правильный путь.

struct abstract {
    virtual int fst_func() = 0;
    virtual void sec_func(int) = 0;
};

// store an instance of "abstract".
class user_of_abstract
{
private:
    abstract* m_abstract;

public:
    // Pass a pointer to an "abstract" object. The caller takes care of the memory resource.
    user_of_abstract_base(abstract* a) : m_abstract(a) { }

    // Pase any type, which needs to be derived from "abstract" and create a copy. Free memory in destructor.
    template<class abstract_type>
    user_of_abstract_base(abstract_type const& a) : m_abstract(new abstract_type(a)) { }

    // use the stored member to call fst_func.
    int use_fst_func() {
        return this->m_abstract->fst_func();
    }

    // use the stored member to call sec_func.
    void use_sec_func(int x) {
        this->m_abstract->sec_func(x);
    }
};

// use boost::shared_ptr
class user_of_abstract
{
private:
    boost::shared_ptr<abstract> m_abstract;

public:
    // Pass a pointer to an "abstract" object. The caller takes care of the memory resource.
    user_of_abstract_base(boost::shared_ptr<abstract> a) : m_abstract(a) { }

    // use the stored member to call fst_func.
    int use_fst_func() {
        return this->m_abstract->fst_func();
    }

    // use the stored member to call sec_func.
    void use_sec_func(int x) {
        this->m_abstract->sec_func(x);
    }
};

// pass a pointer of an "abstract" object wherever needed.
struct user_of_abstract
{
    // use the passed pointer to an "abstract" object to call fst_func.
    int use_fst_func(abstract* a) {
        return a->fst_func();
    }

    // use the passed pointer to an "abstract" object to call sec_func.
    void use_sec_func(abstract* a, int x) {
        a->sec_func(x);
    }
};

Важно отметить, что параметр "x" из sec_func () должен быть значением, возвращаемым функцией fst_func () в том же "абстрактном" экземпляре.

EDIT: Добавлен еще один подход с использованием boost :: shared_ptr, который должен использовать большинство преимуществ.

Ответы [ 3 ]

1 голос
/ 13 июня 2011

Я бы сказал, что передача объекта abstract в конструктор вашего пользователя - это правильный подход, поскольку методы пользователя зависят от вызова одного и того же объекта abstract.Я бы даже пошел дальше и сделал бы параметр x внутренним состоянием вашего пользователя, поскольку вы сказали, что важно, чтобы это значение было возвращено в результате вызова первой функции.

Update : Если вас беспокоит срок службы, вы можете использовать различные опции интеллектуального указателя, например, для повышения.Они должны охватывать большинство сценариев использования.

1 голос
/ 13 июня 2011

Поскольку вы говорите, что вторая функция должна использовать вывод первой. Я думаю, что первый подход уменьшит вероятность ошибок. Вы даже можете изменить его следующим образом:

int use_fst_func() {
    return x=this->m_abstract->fst_func();
}

void use_sec_func() {
    this->m_abstract->sec_func(x);
}

protected:
   int x;
0 голосов
/ 13 июня 2011

Вы ставите себя в море проблем с обслуживанием.

В вашем первом примере ...

есть действительно не нужнодля конструктора шаблона.Он обозначается как

// Parse any type, which needs to be derived from "abstract" and create a copy.

Пользователь уже может сделать это, создав сам экземпляр и передав его первому конструктору.

Также с помощью этого:

// Free memory in destructor.

Вы прямо говорите, что понятия не имеете, как использовать этот класс.Поскольку ваш первый пример написан, вам нужно решить: использовать экземпляр, созданный снаружи, или использовать экземпляр, созданный внутри.Это сбивает с толку видеть интерфейс с одним ctor, принимающим указатель, и другим ctor, принимающим ссылку, оба по существу одного и того же типа.

На мой взгляд, единственный приемлемый способ использования экземпляра, созданного извне,не должен управляться памятью или экземпляр, созданный изнутри, который будет управляться памятью, - это когда есть ctor по умолчанию, который может инициализировать внутренний указатель на разумное значение (но это не похоже наслучай здесь, поскольку вы хотите скопировать другой экземпляр):

template <typename T>
class user_of_abstract
{
    bool m_owner_;
    abstract* m_abstract;

public:

    user_of_abstract_base(abstract* a = NULL)
    : m_owner(a == NULL)
    , m_abstract(m_owner ? new T(): a)
    {
    }

    ~user_of_abstract_base()
    {
        if (m_owner)
        {
            delete m_abstract;
        }
    }
}

Ваш второй пример ...

превосходит первый, так как вы неt явно смешивать управление памятью со ссылкой на память.Вы позволяете shared_ptr делать это неявно.Очень хорошо, вот для чего это.

Однако, поскольку у вас есть требование, чтобы use_sec_func брал в качестве входных данных вывод use_fst_func, вы далеки от безопасного берега моря технического обслуживания.проблемы.

Например, что произойдет, если use_fst_func в экземпляре выдает исключение и use_sec_func позже будет вызван в этом же экземпляре?

Как вы ожидаете, что важная информация "Всегда вызывайте А до Б. И только один раз. И передайте результат А Б.должен распространяться среди пользователей класса через 2 года?

Почему use_sec_func нельзя просто позвонить use_fst_func?

Что касается вашего третьего примера ...

Можете ли вы дать 1 единственный сценарий, когда вы хотите использовать это вместо простого вызова функций экземпляра?

...