我需要在列表末尾添加新项目,删除最后一个并显示整个列表。当显示整个列表时,由于某种原因,仅显示堆栈中的最后一个元素(按列表中的元素数)。为什么?
#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;
}
}
}
此构造函数
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
类型的数据成员的声明。
在构造函数中,:name(name)
不会执行您期望的操作,而name是char*
时,您不会深入复制该名称,而只是将未生效的指针保存在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中访问它们以编写它们也是一个好主意