【问题标题】:Creating a list to dynamically allocate objects in C++ based on user input创建一个列表以根据用户输入在 C++ 中动态分配对象
【发布时间】:2021-09-24 07:33:20
【问题描述】:

我一直在尝试寻找有关如何根据用户输入创建类的新对象的教程或示例。

为了举例说明,我想让用户选择按“1”键来创建“客户”类的新对象,并将客户信息添加为名称、ID 等。 然后通过客户名称或 ID 访问它以显示该对象中存储的所有信息。

我最大的困难是如何创建多个对象并将其分配到一个列表中,我找不到任何关于此的在线教程。

编辑

感谢所有为此提供帮助的人。

我尝试实现下面的代码,但没有成功:

do
{
    cout << "Press 1 to create a new customer";
    switch (opt)
    {
        case 1:
        cout << "Input Customer's name";
        cin >> tempName;
        customer *tempName = new customer();
        tempName -> name = tempName;

    }
} while (opt !=9);

错误:从“客户”到“字符”的无效转换

我的想法是从用户那里接收客户名称,然后创建一个与客户同名的客户类型对象,然后添加更多信息。完成此操作后,我将寻找将对象地址添加到向量或列表。

对于如何实现第一个目标的任何帮助,我将不胜感激。

【问题讨论】:

  • 您知道如何使用std::cin 读取用户的输入吗?你知道如何从一个类中创建对象吗?你知道如何向std::vector 添加对象吗?那么你已经知道了所有你需要知道的,把它们放在一起。
  • 您能否更具体地说明您面临的具体问题?正如您目前所描述的,您的问题似乎归结为仅编写一个 if 条件检查用户输入并创建一个对象(动态)并将指向该对象的指针存储在容器中(最好是 std::vector)。
  • 你在使用哪本 C++ 教科书,它们都应该有很多这样做的例子?
  • 感谢大家,我刚刚更新了我的问题,以便更具体地说明我目前正在努力的问题。 @SamVarshavchik 我正在寻找更多在线信息和视频来学习,但在网上找不到这个主题。我一直在搜索“列出多个对象 c++”和类似
  • 那是因为您在某些网站或随机的 Youtube 视频上找不到有用的 C++ 信息和教程。 C++ 是当今使用的最复杂、最难学习的通用编程语言。任何人都可以建立一个网站并在上面涂鸦;任何小丑都可以将他们的随意胡言乱语上传到 Youtube。但是,有效学习和理解 C++ 的唯一方法是从经过编辑、校对的教科书中制定一个有指导的、有组织的、有条理的学习计划。学习 C++ 没有捷径可走。这需要很多时间,并且需要一本好的教科书作为学习指南。

标签: c++ class dynamic-memory-allocation


【解决方案1】:

你可以使用std::any做我认为你想做的事

void add_item(std::list<std::any>& list) {
    int t = get_type_from_user();
    
    if (t == 1) {
        Customer c = create_customer_from_user();
        list.push_back(std::any(c));
    } else if (t == 2) {
        //insert other type into list
    }
}

Customer get_customer_from_id(const std::list<std::any>& list, int id) {
    for (const auto& entry : list) {
        if (entry.type() == typeid(Customer)) {
            auto c = std::any_cast<Customer>(entry);
            if (c.id() == id)
                return c;
        }
    }

    throw CustomerNotFound();
}

如果您不需要它是 std::list std::vector 会更好。

【讨论】:

    【解决方案2】:

    您正在寻找的模式称为 factoryabstract factory。您事先并不知道要创建什么对象。因此,用户将输入一些数字,并且依赖于此,您将添加相应的对象并设置其参数。

    当然,所有可能的不同类都需要在您的代码之前定义,但它们将在运行时动态创建和实例化。

    为此,通常使用std::mapstd::unordered 映射。在关键部分,您将存储一个选择器。在您的情况下,用户将输入一个数字,例如 1 或 2 或 3 等等。在映射的值部分,您将存储一个指向创建函数的函数指针。

    create 函数将在运行时动态创建一个对象,late。并且因为 create 函数的签名(尤其是返回值)必须是相同的类型,所以我们将使用多态性,并从一个公共基类派生所有您需要的类。

    然后我们可以将所有对象作为指向基类的指针存储在容器中,并通过它们的基类指针调用成员函数。

    这是一般概念。派生类的构造函数通常会有所不同,因为您希望为每个类传递一组特定的参数。然后它变得更加复杂。但也可以解决:

    请参阅下面的,抱歉,更复杂的解决方案示例:

    #include <iostream>
    #include <map>
    #include <utility>
    #include <any>
    
    
    // Some demo classes ----------------------------------------------------------------------------------
    struct Base {
        Base(int d) : data(d) {};
        virtual ~Base() { std::cout << "Destructor Base\n"; }
        virtual void print() { std::cout << "Print Base\n"; }
        int data{};
    };
    struct Child1 : public Base {
        Child1(int d, std::string s) : Base(d) { std::cout << "Constructor Child1 " << d << " " << s << "\n"; }
        virtual ~Child1() { std::cout << "Destructor Child1\n"; }
        virtual void print() { std::cout << "Print Child1: " << data << "\n"; }
    };
    struct Child2 : public Base {
        Child2(int d, char c, long l) : Base(d) { std::cout << "Constructor Child2 " << d << " " << c << " " << l << "\n"; }
        virtual ~Child2() { std::cout << "Destructor Child2\n"; }
        virtual void print() { std::cout << "Print Child2: " << data << "\n"; }
    };
    struct Child3 : public Base {
        Child3(int d, long l, char c, std::string s) : Base(d) { std::cout << "Constructor Child3 " << d << " " << l << " " << c << " " << s << "\n"; }
        virtual ~Child3() { std::cout << "Destructor Child3\n"; }
        virtual void print() { std::cout << "Print Child3: " << data << "\n"; }
    };
    
    
    
    using UPTRB = std::unique_ptr<Base>;
    
    
    template <class Child, typename ...Args>
    UPTRB createClass(Args...args) { return std::make_unique<Child>(args...); }
    
    // The Factory ----------------------------------------------------------------------------------------
    template <class Key, class Object>
    class Factory
    {
        std::map<Key, std::any> selector;
    public:
        Factory() : selector() {}
        Factory(std::initializer_list<std::pair<const Key, std::any>> il) : selector(il) {}
    
        template<typename Function>
        void add(Key key, Function&& someFunction) { selector[key] = std::any(someFunction); };
    
        template <typename ... Args>
        Object create(Key key, Args ... args) {
            if (selector.find(key) != selector.end()) {
                return std::any_cast<std::add_pointer_t<Object(Args ...)>>(selector[key])(args...);
            }
            else return nullptr;
        }
    };
    
    int main()
    {
        // Define the factory with an initializer list
        Factory<int, UPTRB> factory{
            {1, createClass<Child1, int, std::string>},
            {2, createClass<Child2, int, char, long>}
        };
    
        // Add a new entry for the factory
        factory.add(3, createClass<Child3, int, long, char, std::string>);
    
    
        // Some test values
        std::string s1(" Hello1 "); std::string s3(" Hello3 ");
        int i = 1;  const int ci = 1;   int& ri = i;    const int& cri = i;   int&& rri = 1;
    
        UPTRB b1 = factory.create(1, 1, s1);
        UPTRB b2 = factory.create(2, 2, '2', 2L);
        UPTRB b3 = factory.create(3, 3, 3L, '3', s3);
    
        b1->print();
        b2->print();
        b3->print();
        b1 = factory.create(2, 4, '4', 4L);
        b1->print();
        return 0;
    }
    

    可以使用您的选择器和不同的参数调用创建函数。

    通过基类指针调用print函数,总会调用正确的函数。

    请阅读工厂模式以获得更好的理解。

    【讨论】:

      【解决方案3】:

      在不知道customer 的确切类型的情况下,可以说它是这样的:

      class customer
      {
      public:
          customer() { }
      
          std::string name;
      };
      

      那么我的建议是修改它,以便隐藏数据成员,并使用构造函数对其进行初始化:

      class customer
      {
      public:
          customer(std::string const& name)
              : name(name)
          { }
      
      private:
          std::string name;
      };
      

      然后您可以使用std::vector&lt;customer&gt; 并在循环中创建任意数量的对象:

      std::vector<customer> customers;
      
      // Example loop, just to show how to add objects to the vector
      for (unsigned i = 0; i < 10; ++i)
      {
          std::cout << "Please enter name of customer #" << i + 1 << ": ";
      
          std::string name;
          std::cin >> name;
      
          customers.emplace_back(name);
      }
      

      在这个循环之后,向量中有十个customer 对象,每个对象都有自己的名称。


      老实说,任何体面的书籍或课程都应该教会你这一点。

      【讨论】:

        猜你喜欢
        • 2018-03-12
        • 2021-08-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-18
        • 2017-09-11
        相关资源
        最近更新 更多