【问题标题】:A vector for different classes不同类别的向量
【发布时间】:2018-09-12 17:33:40
【问题描述】:

我有一个带有向量的类,我想用用户选择的两种类中的一种来填充。让我们将我的课程称为 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。

【问题讨论】:

  • 您使用的是哪个特定版本的 C++?
  • 您能告诉我们有关option1option2 的哪些信息?它们是完全不相关的两种类型吗?必须出于某种原因(以及为什么)在堆上创建它们吗?它们是否都源自一些常见的base
  • @JackEvans -- 需要更多信息。贾斯汀提出了相关问题。
  • C++17 可用,看看 std::variant(例如en.cppreference.com/w/cpp/utility/variant
  • @JackEvans 好的,完成。希望你觉得它有用。

标签: c++ templates c++17


【解决方案1】:

由于您使用的是 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_ptrstd::shared_ptr ;)

【讨论】:

  • 一个尖端的解决方案,不错
  • @neonxc: :-) 谢谢!
【解决方案2】:

这是一个示例,它试图尽可能接近您的示例,它可以使用类 storage_class 上的模板参数。请参阅工作版本here。我只添加了option1,并在您在main 函数中访问成员my_store 时将其设为公开。

#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。参见 workign 版本here

#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();
    }
}

【讨论】:

    【解决方案3】:

    标准方法是从base_class 生成option1option2 派生类,这似乎与您的示例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() 函数以说明基本思想。希望对您有所帮助。


    2018 年 9 月 21 日编辑 - 如何将参数传递给构造函数的示例。

    文件: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)
    

    【讨论】:

    • 使用这个方法我怎样才能传递给我的选项类的构造函数?
    • 添加了一个示例,用于将单个 double 传递给构造函数,但您可以添加任意数量的参数。
    • 太棒了。哪种解决方案最适合您的需求,为什么?
    猜你喜欢
    • 1970-01-01
    • 2018-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-07
    • 1970-01-01
    相关资源
    最近更新 更多