Вектор для разных классов - PullRequest
       0

Вектор для разных классов

0 голосов
/ 12 сентября 2018

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

Что бы я хотел сделать, что-то вроде

class storage_class 
{
public:
    storage_class(int sel, int n)
    {
        if(sel == 1)
           for(int i = 0; i < n; i++) 
               my_store.push_back(std::make_unique<option1>());    
        else if(sel == 2)
           for(int i = 0; i < n; i++)
               my_store.push_back(std::make_unique<option2>());
    }

private:
    // Something like this but that actually works
    std::vector<T> my_store;
};

Тогда я бы хотел использовать это так, или что-то подобное, так что нетнеобходимо изменить это использование в зависимости от выбранной опции.

int main()
{
    storage_class store(1);

    int n_iterations = 4;

    for(int i = 0; i < n_iterations; i++)
    {
        store.my_store[i]->create_data();
    }
}

Классы option1 и option2 будут математическим моделированием, которое будет создавать данные и сами хранить эти данные в векторе, являющемся членами класса.

Я хочу сохранить несколько экземпляров любой опции в векторе, а затем манипулировать ими оттуда.Я могу использовать C ++ 17.

Ответы [ 3 ]

0 голосов
/ 12 сентября 2018

Поскольку вы используете c ++ 17, вы можете просто использовать std::variant в качестве типа для контейнера, который сам может хранить все типы, которые вы хотите иметь.

Пример:

class A { public: void Do() { std::cout << "A::Do" << std::endl; } };
class B { public: void Go() { std::cout << "B::Go" << std::endl; } };


template<class... Ts> struct funcs : Ts... { using Ts::operator()...; };
template<class... Ts> funcs(Ts...) -> funcs<Ts...>;

int main()
{
    std::vector<std::variant<A,B>> vec;
    vec.push_back(A{});
    vec.push_back(B{});

    for ( auto& el: vec)
    {
        std::visit( funcs{ [](A& a){ a.Do(); }, [](B& b) { b.Go(); } }, el);
    }
}

Выход:

A::Do
B::Go

Классы полностью независимы, и методы могут быть просто вызваны с помощью std::visit и передачей вызываемого объекта. Я предоставляю простую реализацию funcs, которая просто собирает все вызываемые объекты, чтобы упростить взаимодействие вызова с различными методами различных несвязанных классов.

Поскольку std::variant является неким теговым объединением, ему требуется хранилище для самого большого используемого вами типа. Если это приводит к потере большого объема памяти, вы можете вместо этого сохранить указатель на экземпляр, возможно, с помощью std::unique_ptr или std::shared_ptr, если вам нужна помощь в управлении памятью;)

0 голосов
/ 18 сентября 2018

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

#include <memory>
#include <vector>
#include <iostream>

struct option1{
    void create_data(){ std::cout << "created\n"; }
};


template<typename T>
class storage_class 
{
public:
    storage_class(int n)
    {
       for(int i = 0; i < n; i++) 
           my_store.push_back(std::make_unique<T>());    
    }

    std::vector<std::unique_ptr<T>> my_store;
};

int main()
{
    storage_class<option1> store(4);

    int n_iterations = 4;

    for(int i = 0; i < n_iterations; i++)
    {
        store.my_store[i]->create_data();
    }
}

другой вариант - использовать std::variant.Смотрите рабочую версию здесь .

#include <memory>
#include <vector>
#include <variant>
#include <iostream>

struct option1{
    void create_data(){ std::cout << "created 1\n"; }
};

struct option2{
    void create_data(){ std::cout << "created 2\n"; }
};


class storage_class 
{
public:

    using option = std::variant<std::unique_ptr<option1>,std::unique_ptr<option2>>;

    storage_class(int sel, int n)
    {
        if(sel == 0)
           for(int i = 0; i < n; i++) 
               my_store.push_back(option(std::make_unique<option1>()));    
        else if(sel == 1)
           for(int i = 0; i < n; i++)
               my_store.push_back(option(std::make_unique<option2>()));
    }


    std::vector<option> my_store;
};

int main()
{
    storage_class store(1, 4);

    int n_iterations = 4;

    for(int i = 0; i < n_iterations; i++)
    {
        std::get<1>(store.my_store[i])->create_data();
    }
}
0 голосов
/ 12 сентября 2018

Стандартным способом является создание option1 и option2 производных классов из base_class, что, по-видимому, соответствует вашему образцу main(). Используя общий шаблон класса Factory, вот пример:

#include <functional>
#include <iostream>
#include <memory>
#include <unordered_map>
#include <vector>

// Generic Factory class template
template<typename K,typename T,typename... Ts>
class Factory
{
    using Map = std::unordered_map<K, std::function<std::unique_ptr<T>(Ts...)>>;
    const Map mMap;
  public:
    Factory(Map&& map):mMap(std::move(map)) { }
    std::unique_ptr<T> operator()(const K& key, Ts... args) const
    {
        const typename Map::const_iterator itr = mMap.find(key);
        return itr == mMap.cend() ? nullptr : itr->second(std::forward<Ts>(args)...);
    }
};

class base_class
{
  public:
    virtual void create_data() = 0;
};

class option1 : public base_class
{
  public:
    void create_data() override
    {
        std::cout << "I'm option1." << std::endl;
    }
};

class option2 : public base_class
{
  public:
    void create_data() override
    {
        std::cout << "I'm option2." << std::endl;
    }
};

class storage_class 
{
    using SimulationFactory = Factory<int,base_class>; // Optionally add constructor parameter types
    const SimulationFactory simulation_factory; // This can be made static const.
public:
    storage_class(int sel, int n)
    :   simulation_factory(
            { { 1, []() { return std::make_unique<option1>(); } }
            , { 2, []() { return std::make_unique<option2>(); } }
            })
    {
        for (int i = 0; i < n; i++) 
            my_store.push_back(simulation_factory(sel));
    }

    std::vector<std::unique_ptr<base_class>> my_store;
};

int main()
{
    int n_iterations = 4;

    storage_class store(1, n_iterations);

    for(int i = 0; i < n_iterations; i++)
    {
        store.my_store[i]->create_data();
    }
}

Это скомпилировано для меня на Linux с использованием g++ -std=c++17 main.cc.

В этот код можно внести улучшения, но я скопировал вашу функцию main(), чтобы проиллюстрировать основные идеи. Надеюсь, это поможет.


Редактировать 21 сентября 2018 г. - Пример передачи параметров в конструкторы.

Файл: factory.h

#pragma once

#include <functional>
#include <memory>
#include <unordered_map>

// Generic Factory class template
template<typename K,typename T,typename... Ts>
class Factory
{
    using Map = std::unordered_map<K, std::function<std::unique_ptr<T>(Ts...)>>;
    const Map mMap;
  public:
    Factory(Map&& map):mMap(std::move(map)) { }
    std::unique_ptr<T> operator()(const K& key, Ts... args) const
    {
        const typename Map::const_iterator itr = mMap.find(key);
        return itr == mMap.cend() ? nullptr : itr->second(std::forward<Ts>(args)...);
    }
};

Файл: main.cc

#include "factory.h"

#include <iostream>
#include <string>
#include <vector>

class base_class
{
  public:
    virtual void create_data() = 0;
};

class option1 : public base_class
{
    const double mD;
  public:
    option1(double d)
    :   mD(d)
    { }
    void create_data() override
    {
        std::cout << "I'm option1: mD("<<mD<<')' << std::endl;
    }
};

class option2 : public base_class
{
    const double mD;
  public:
    option2(double d)
    :   mD(d)
    { }
    void create_data() override
    {
        std::cout << "I'm option2: mD("<<mD<<')' << std::endl;
    }
};

class storage_class 
{
    using SimulationFactory = Factory<int,base_class,double>; // Optionally add constructor parameter types
    const SimulationFactory simulation_factory; // This can be made static const.
public:
    storage_class(int sel, int n)
    :   simulation_factory(
            { { 1, [](double d) { return std::make_unique<option1>(d); } }
            , { 2, [](double d) { return std::make_unique<option2>(d); } }
            })
    {
        for (int i = 0; i < n; i++) 
            my_store.push_back(simulation_factory(sel,static_cast<double>(i)));
    }

    std::vector<std::unique_ptr<base_class>> my_store;
};

int main()
{
    int n_iterations = 4;

    storage_class store1(1, n_iterations);
    storage_class store2(2, n_iterations);

    for(int i = 0; i < n_iterations; i++)
    {
        store1.my_store[i]->create_data();
        store2.my_store[i]->create_data();
    }
}

Выход:

I'm option1: mD(0)
I'm option2: mD(0)
I'm option1: mD(1)
I'm option2: mD(1)
I'm option1: mD(2)
I'm option2: mD(2)
I'm option1: mD(3)
I'm option2: mD(3)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...