【问题标题】:When the entire stack is displayed, only the last elements of the stack are displayed. Why?当显示整个堆栈时,仅显示堆栈的最后一个元素。为什么?
【发布时间】:2020-06-07 22:01:11
【问题描述】:

我需要在列表末尾添加新项目,删除最后一项并显示整个列表。 在显示整个列表时,由于某种原因,仅显示堆栈的最后一个元素,按列表中的元素数量计算。 为什么?

#include <iostream>
#include <stdio.h>
#include <stack>

using namespace std;

struct Node
{
    char* name = new char[6];
    float sumary;
    int amount;
    Node(char* name, float sumary, int amount) :name(name), sumary(sumary), amount(amount)
    {
    }
    Node() {}
};

int main()
{
    int command;
    stack<Node> node;
    for (;;)
    {
        printf("Input command:\n 1 - add,\n 2 - delete last,\n 3 - show all,\n 4 - exit\n");
        scanf("%d", &command);
        switch (command)
        {
        case 1:
            char name[6];
            float sumary;
            int amount;
            printf("Enter name: ");
            scanf("%s", &name);
            printf("Enter sumary: ");
            scanf("%f", &sumary);
            printf("Enter amount: ");
            scanf("%d", &amount);
            node.push(Node(name, sumary, amount));
            break;
        case 2:
            node.pop();
            printf("The last have been deleted");
            break;
        case 3:
            while (!node.empty())
            {
                Node temp = node.top();
                node.pop();
                cout << temp.name << " " << temp.sumary << " " << temp.amount << endl;
            }
            break;
        case 4:
            return 0;
        default:
            printf("Wrong command...");
            break;
        }
    }
}

【问题讨论】:

  • 使用调试器逐行遍历它或在循环中打印有趣的值。当它开始行为不端时,你会得到一个线索。

标签: c++ class dynamic-memory-allocation construct ctor-initializer


【解决方案1】:

在构造函数中:name(name) 没有按照您的预期进行,而 namechar*,您不会深度复制名称,而只是将无效的指针保存在 @987654323 之外@ 具有未定义的行为(并且丢失了分配的 char 数组,从而导致内存泄漏)。

使用std::string 而不是char*,您将获得预期的行为。这也大大简化了 Node 被复制、分配、删除时的管理,因为您无事可做,而使用指针则不是这样。

如果有超过 5 个字符的单词可用于 scanf("%s", &amp;name);,那么除了具有 char name[6]; 之外,它会以未定义的行为从您的数组中写入。

您不会同时检查scanf 的调用是否成功,检查它们是否返回 1,如果输入无效,您将不受保护。

请注意,您可以使用 iostream 功能,而不是使用 C 函数来读取/写入(也可以检查读取是否成功)。

选择std::stack 而不是std::vector 很奇怪,因为编写它的内容不太实用。

一种方法可以是在写入其包含时不清空堆栈:

#include <iostream>
#include <string.h>
#include <stack>
#include <limits>

struct Node
{
  std::string name;
  float sumary;
  int amount;

  Node(std::string name, float sumary, int amount) 
    :name(name), sumary(sumary), amount(amount) {
 }
 Node() {}
};

void inputError(const char * msg)
{
  if (std::cin.rdstate() & std::istream::eofbit)
  {
    // EOF
    std::cerr << "EOF, abort" << std::endl;
    exit(-1);
  }

  std::cerr << msg << std::endl;

  std::cin.clear();
  // flush all the line, you can just read a word
  // with "string s; cin >> s;" if you prefer
  std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

int main()
{
    int command;
    std::stack<Node> node;

    for (;;)
    {
        std::cout << "Input command:\n 1 - add,\n 2 - delete last,\n 3 - show all,\n 4 - exit" << std::endl;

        if (!(std::cin >> command))
          inputError("invalid command, not an integer");
        else
        {
          switch (command)
          {
          case 1:
            {
              std::string name;
              float sumary;
              int amount;

              if (!(std::cout << "Enter name: ", std::cin >> name) ||
                  !(std::cout << "Enter sumary: ", std::cin >> sumary) ||
                  !(std::cout << "Enter amount: ", std::cin >> amount))
                inputError("invalid value");
              else
                node.push(Node(name, sumary, amount));
            }
            break;
          case 2:
            if (node.empty())
              std::cerr << "no node to delete" << std::endl;
            else 
            {
              node.pop();
              std::cout << "The last have been deleted" << std::endl;
            }
            break;
          case 3:
            {
              std::stack<Node> temp = node;

              while (!temp.empty())
              {
                const Node & n = temp.top();

                temp.pop();
                std::cout << n.name << " " << n.sumary << " " << n.amount << std::endl;
              }
            }
            break;
          case 4:
            return 0;
          default:
            std::cerr << "Wrong command number" << std::endl;
            break;
          }
        }
    }
}

编译和执行:

pi@raspberrypi:/tmp $ g++ -Wall c.cc
pi@raspberrypi:/tmp $ ./a.out
Input command:
 1 - add,
 2 - delete last,
 3 - show all,
 4 - exit
7
Wrong command number
Input command:
 1 - add,
 2 - delete last,
 3 - show all,
 4 - exit
z
invalid command, not an integer
Input command:
 1 - add,
 2 - delete last,
 3 - show all,
 4 - exit
1
Enter name: aze
Enter sumary: a
invalid value
Input command:
 1 - add,
 2 - delete last,
 3 - show all,
 4 - exit
1
Enter name: aze
Enter sumary: 1.1
Enter amount: 2
Input command:
 1 - add,
 2 - delete last,
 3 - show all,
 4 - exit
1
Enter name: qsd
Enter sumary: 2.2
Enter amount: 3
Input command:
 1 - add,
 2 - delete last,
 3 - show all,
 4 - exit
3
qsd 2.2 3
aze 1.1 2
Input command:
 1 - add,
 2 - delete last,
 3 - show all,
 4 - exit
3
qsd 2.2 3
aze 1.1 2
Input command:
 1 - add,
 2 - delete last,
 3 - show all,
 4 - exit
2
The last have been deleted
Input command:
 1 - add,
 2 - delete last,
 3 - show all,
 4 - exit
3
aze 1.1 2
Input command:
 1 - add,
 2 - delete last,
 3 - show all,
 4 - exit
4
pi@raspberrypi:/tmp $ 

Node 中定义 operator&lt;&lt; 也是一个好主意,而不是将其成员公开以便能够在 main 中访问它们来编写它们

【讨论】:

    【解决方案2】:

    这个构造函数

    Node(char* name, float sumary, int amount) :name(name), sumary(sumary), amount(amount)
    {
    }
    

    没有意义,因为在类定义中初始化数据成员名称

    char* name = new char[6];
    

    被忽略。来自 C++ 17 标准(12.6.2 初始化基和成员)

    10 如果给定的非静态数据成员同时具有 大括号或相等初始化器和一个内存初始化器,初始化 执行由 mem-initializer 指定的非静态数据 成员的大括号或相等初始化器被忽略。

    所以数据成员name总是由本地数组name的第一个元素的地址初始化并自动存储时长

        switch (command)
        {
        case 1:
            char name[6];
            //...
            node.push(Node(name, sumary, amount));
            //...
    

    所以程序有未定义的行为。结果,正如您自己指出的那样,您会发现所有节点都输出相同的字符串。

    注意这个scanf的调用

    scanf("%s", &name);
    

    不正确。你必须写

    scanf("%s", name);
    

    但无论如何,使用 C++ 流函数要好得多。而不是动态分配的数组,您应该使用标准类std::string

    此外,如果您使用动态内存分配,您的类没有显式析构函数来释放分配的内存。

    所以用指针声明代替std::string类型的数据成员的声明。

    【讨论】:

      猜你喜欢
      • 2014-09-09
      • 2017-11-11
      • 2021-12-06
      • 2018-03-18
      • 2016-03-13
      • 1970-01-01
      • 1970-01-01
      • 2013-12-12
      • 2013-02-21
      相关资源
      最近更新 更多