当显示整个堆栈时,仅显示堆栈的最后一个元素。为什么?

问题描述 投票:0回答:2

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

#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
2个回答
0
投票

此构造函数

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初始化程序,初始化 执行由mem-initializer指定的非静态数据 成员的大括号或相等初始化器将被忽略。

因此,数据成员name始终由具有自动存储持续时间的本地数组name的firs元素的地址初始化

    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类型的数据成员的声明。


0
投票

在构造函数中,:name(name)不会执行您期望的操作,而namechar*时,您不会深入复制该名称,而只是将未生效的指针保存在switch中,并且未定义行为(并丢失了char的分配数组,从而导致内存泄漏)。

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

[具有char name[6];的单词中,如果一个单词具有5个以上的字符可用于scanf("%s", &name);,则它以不确定的行为写出数组。

您也不会检查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<<,而不是公开其成员以便能够在main中访问它们以编写它们也是一个好主意

© www.soinside.com 2019 - 2024. All rights reserved.