Различные шаблонные классы в наборе с использованием boost :: shared_ptr - PullRequest
1 голос
/ 04 октября 2011

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

Я попытался обобщить мою проблему в этом простом примере:

У меня есть два разных классаDerivedOne и DerivedTwo, которые наследуются от одного и того же класса Base и разделяют определение метода.
У меня есть набор shared_ptr, указывающий на client, у которого есть один объект из двух разных классов DerivedOne и DerivedTwo.
Поскольку я не хочу использовать указатель Base* для хранения этого 2 разных классов, я попытался создать шаблон класса клиента.

Но у меня есть 2 разных класса, и я не могу держать их в одном наборе.
Я думал, shared_ptr может содержать указатель на объект без указания аргумента шаблона, но я ошибся илиЯ не знаю, как это сделать.

Другое решение, которое я вижу, состоит в том, чтобы разделить эти 2 разных client на 2 разных set

Заранее спасибо за любые предложения.

Вот код:

#include <iostream>
#include <set>
#include <boost/shared_ptr.hpp>

class Base
{
    public:
        virtual void test() = 0;
};

class Derived_one
    : public Base
{
    public:
        void test() {
            std::cout << "Derived One" << std::endl;
        }            
};

class Derived_two
    : public Base
{
    public:
        void test() {
            std::cout << "Derived Two" << std::endl;
        }
};

template <class temp_arg>
class Client
{
    public:        
        int test(){
            obj_.test();
        }

    protected:
        temp_arg obj_;

};

typedef Client<Derived_one> ClientOne;
typedef Client<Derived_two> ClientTwo;    

/// Here I don't want to specify any of the two previously declared client :
//typedef boost::shared_ptr<Client> Client_ptr;
typedef boost::shared_ptr<ClientOne> Client_ptr;

int main(int, const char**)
{
    std::set<Client_ptr> Clients_;
    Client_ptr client_(new ClientOne());

    Clients_.insert(client_);
    client_->test();

    return 0;
}

Если вы хотите взглянуть на реальный код:
https://github.com/gravitezero/Node/tree/experimental/src
Client соответствует connection
Класс Base равен message
Два производных класса: peply и request.

Ответы [ 2 ]

0 голосов
/ 04 октября 2011

Улучшенные общие указатели шаблонов не будут выполнять магию, которую вы не сможете сделать без обычных указателей (а именно, поместить два разных типа в одну коллекцию). Всякий раз, когда вы используете шаблон, вы должны явно объявить аргументы шаблона, потому что все, что делает шаблон - это генерирует дублирующий код для вас во время компиляции. Поэтому, когда вы используете шаблон, компилятор просто дублирует файл шаблона столько раз, сколько вы объявили с аргументами шаблона. Нет аргументов шаблона, нет дублирования, нет шаблона. По крайней мере, это мое понимание, я уверен, что полиция C ++ исправит меня, если я ошибаюсь.

Таким образом, способ поместить два разных типа в одну коллекцию - это замаскировать значение, помещаемое в коллекцию. Самый простой способ сделать это (и плохой способ) - создать коллекцию пустых указателей, а затем использовать вставку и извлечение в зависимости от контекста. Это опасно, потому что тогда вы будете получать доступ к памяти через указатель без проверки того, что указатель указывает на правильный объем / формат памяти (это проверка типа).

Итак, реальный способ сделать это, как предложил пмр. Сделайте свою коллекцию коллекцией указателей базового типа и используйте динамическое приведение для приведения между типами при вставке и извлечении из списка. Динамическое приведение проверит во время выполнения, чтобы убедиться, что тип, из которого вы преобразуете, и из которого совместимы. То есть, если вы преобразуете из Derived1 в Base, а затем из Base в Derived1, это нормально. Если вы преобразуете из Derived1 в базу, а затем из базы в Derived2 по указателю, указывающему на ту же память, во время выполнения произойдет сбой, поскольку Derived2 и Derived1 несовместимы.

Это объяснение немного длинное, потому что у меня была такая же проблема с дизайном несколько месяцев назад, пока я не прочитал немного.

0 голосов
/ 04 октября 2011

Почему вы не хотите использовать указатель на экземпляр Base? Если вы используете runtime-полиморфизм, это единственный способ. Если вам на самом деле не нужен полиморфизм времени выполнения, виртуальные функции-члены - неправильный путь.

Просто используйте boost::scoped_ptr или (лучше) C ++ 11 std::unique_ptr, чтобы сохранить base* в Client.

Если вы решите отказаться от полиморфизма времени выполнения, вам следует изучить CRTP для реализации статического полиморфизма.

...