【问题标题】:How to correctly read inputs into a dynamically allocated array of objects如何正确地将输入读入动态分配的对象数组
【发布时间】:2021-03-13 16:10:30
【问题描述】:

我目前正在做一个项目,它是一个门票存储系统,将每个持票人存储为一个对象。

为了输入值,我使用了参数化构造函数。在主函数中,我为这些对象的数组声明了一个动态内存块。

我面临的主要问题是,在初始化每个对象的 for 循环中,循环只运行一次,然后终止。代码如下:

#include <iostream>
#include <stdlib.h>
#include <string>

using namespace std;
class node
{
    string holder_name;
    int age;

public:
    node(string a, int b)
    {
        holder_name = a;
        age = b;
    }

    void view()
    {
        cout << "Name: " << holder_name << endl;
        cout << "Age: " << age << endl;
    }
};
int main()
{
    int count, i;
    cout << "Enter no of nodes" << endl;
    cin >> count;
    node *arr = (node *)malloc(sizeof(node) * count);

    for (i = 0; i < count; i++)
    {
        int b;
        string str;
        cout << "Enter name" << endl;
        cin >> str;
        cout << "Enter age" << endl;
        cin >> b;
        arr[i] = node(str, b);
        arr[i].view();
    }
    return 0;
}

【问题讨论】:

  • 不要在 C++ 中使用malloc,而是使用std::vector
  • for 循环取决于用户输入。要获得更好的 minimal reproducible example,请删除该因素。 (如果我选择输入1,那么循环应该只运行一次。)而不是int count; cin &gt;&gt; count; 硬编码一个值,如int count = 2;。您还可以从循环中删除用户输入,也许每个人的名字都是"Someone",他们的年龄是i

标签: c++ oop data-structures memory-management malloc


【解决方案1】:

这是一个很好的例子,为什么在 C++ 中不使用 malloc,当你为 nodes 分配内存时,元素没有被初始化。

C++ 中的分配是使用 new 完成的,可以防止上述问题:

node *arr= new node[count];

在这种情况下,您还需要为您的类添加一个默认构造函数

node() = default;node(){};

然而,更好的解决方案是使用可调整大小的containers provided by C++ 容器库之一,例如std::vector

#include <vector>
int main()
{
    int count, i;
    cout << "Enter no of nodes" << endl;
    cin >> count;
    std::vector<node> arr; // now you have a resizable container
    for (i = 0; i < count; i++)
    {
        int b;
        string str;
        cout << "Enter name" << endl;
        cin >> str;
        cout << "Enter age" << endl;
        cin >> b;
        arr.push_back(node(str, b)); // keep adding elements
        arr[i].view();
    }
}

在这里,您还可以按照 S.M. 中的建议创建具有初始大小的向量。答案,它确实取决于您对容器的期望大小,如果它在较大的一侧更好,因为它避免了内存重新分配,在这种情况下,插入将与您在原始代码中的完全一样。

脚注

我要补充一点,using namespace std; 不是最推荐的技术,你可以在这里找到推理和替代方法:

Why is "using namespace std;" considered bad practice?

【讨论】:

    【解决方案2】:

    赋值arr[i]=node(str,b) 需要左侧的有效(构造)值。 malloc 分配数组内存但不初始化数组元素。 C++ new 操作符就是为此目的。

    node *arr= new node[count];
    

    只是为了比较表达式

    node *arr= (node*)malloc(sizeof(node)*count);
    node *arr= new node[count];
    

    第二个表达式比使用的 C malloc 更短,更安全,并且可以自动完成很多工作。但如果您不处理手动内存管理,那就更好了。

    auto arr= std::vector<node>(count);
    

    【讨论】:

      【解决方案3】:

      在你的类中使用构造函数和析构函数。

          class node{
      string holder_name;
      int age;
      public:
      node(){
          
      }
      node(string a, int b)
      {
          holder_name=a;
          age=b;
      }
      
      void view()
      {
          cout<<"Name: "<<holder_name<<endl;
          cout<<"Age: "<<age<<endl;
      }
      ~node()
      {
          
      }
      

      在 c++ 中也不要使用 malloc 进行分配 而是使用

          node*  arr= new node[count];
      

      【讨论】:

      • 在这种情况下,不需要显式定义析构函数。编译器生成的析构函数就足够了 (rule of zero)。
      【解决方案4】:

      如下编辑(对代码的更改最少)。

      #include <iostream>
      #include <stdlib.h>
      #include <string>
      
      using namespace std;
      class node
      {
          string* holder_name;
          int age;
      public:
          node(string a, int b)
          {
              holder_name = new string(a);
              age = b;
          }
          ~node()
          {
              Dispose();
          }
      
          void Dispose()
          {
              if (holder_name)
              {
                  delete holder_name;
                  holder_name = NULL;
              }
          }
      
          void view()
          {
              cout << "Name: " << *holder_name << endl;
              cout << "Age: " << age << endl;
          }
      
          // copy assignment
          void operator = (const node& D) {
              if (holder_name)
                  delete holder_name;
              holder_name = new string( *D.holder_name );
              age = D.age;
          }
      };
      
      int main()
      {
          int count, i;
          cout << "Enter no of nodes" << endl;
          cin >> count;
          node* arr = (node*)malloc(sizeof(node) * count);
          memset(arr, 0, sizeof(node) * count);
          
      
          for (i = 0; i < count; i++)
          {
              int b;
              string str;
              cout << "Enter name" << endl;
              cin >> str;
              cout << "Enter age" << endl;
              cin >> b;
              arr[i] = node(str, b);
              arr[i].view();
          }
      
          
          // avoid memory leak
          for (i = 0; i < count; i++)
              arr[i].Dispose();
          free(arr);
      
          return 0;
      }
      

      【讨论】:

      • 为什么会有帮助?仅代码的答案往往是神秘的,导致cargo cult programming 而不是教育。尝试写出您更改的内容和原因的描述,将实际代码降级为仅对答案的说明,而不是答案本身。
      • 您认为我的修正案没有教育意义吗?例如运算符重载。感谢您的关注@JaMiT
      • 顺便说一句,在我的帖子之后,作为答案的代码已被修改(带有附加说明)。在此之前,有一些秘密点(如默认构造函数)让我发送了这个答案。请注意我的帖子后答案的更正时间。当然,解决方案最好有解释。希望越来越好。 @JaMiT
      • “你认为我的修正案没有教育意义吗?” 这就是我所要理解的要点,是的。没有解释,就可能没有教育。 (对于它的价值:否决票不是来自我。其他人显然也不赞成这个答案,但拒绝解释原因。)
      • 嗯...也许我应该把这个问题理解为“你认为我的修正案中没有潜在教育点吗?”修改后的问题,我的回答是我不知道。我无法通过阅读您的答案来判断您的修改是什么,而且我没有足够的动力将您的代码与问题的代码进行比较以查看您所做的更改。对不起。
      猜你喜欢
      • 1970-01-01
      • 2023-03-27
      • 2011-05-13
      • 1970-01-01
      • 2020-08-31
      • 2019-04-07
      • 1970-01-01
      • 2013-10-06
      • 1970-01-01
      相关资源
      最近更新 更多