我们正在尝试返回一个结构数组来打印main中的内容。在调试代码时,我们到了return语句之前的那一行,它显示它保存了int
的正确内容,然后是string
(playerID
和{{ 1}})。一旦return语句执行,数组就会返回到name
,但只有main
保留在数组中。 playerID
的所有值都已丢失。有人能解释为什么会发生这种情况并提供可能的解决方如果需要进一步说明,请告诉我。
name
答案 0 :(得分:4)
这段代码看起来很像C代码。在C ++中,我们有像std::vector
和std::array
这样的RAII容器,它们可以完全按照您的需要进行操作。
至于问题,你没有返回一个数组,而是返回一个指向int
的指针。你应该看看What is array decaying?。
http://en.cppreference.com/w/cpp/container/vector
http://en.cppreference.com/w/cpp/container/array(C ++> = 11)
答案 1 :(得分:0)
通常正确的答案是使用std::vector
,因为它有助于管理所有权和存储的生命周期,以及跟踪容量并根据需要调整自身大小。但是,让我们一起走过fillPlayers
来解释出现了什么问题。
Player * fillPlayers()
{
ifstream file1;
Player * fullPlayerPointer = new Player[244];
动态分配244 Player
秒。动态存储具有手动控制的生命周期,并且在释放之前保持有效。不幸的是,这个分配的存储空间永远不会被使用,以后不能正确放置delete[]
这个分配将被泄露"。听起来很可怕,这就是"对"分配存储的方法,但正如我们将看到的,该函数错误地使用它。
Player ourPlayers[244];
静态分配244 Player
作为临时变量。此变量仅存在于最内层的大括号{}
内。之后它将被视为无效。这意味着不应该从此函数返回对ourPlayers
的引用,因为ourPlayers
在函数返回时以及调用者可以使用它之前将变为无效。
file1.open("Players.txt");
if (file1.fail())
{
cerr << "file1 did not open";
}
使用前一定要测试,所以上面几乎是正确的。执行几乎相同的测试的下一行使其变得多余。这可能是放置else
的好地方,但使用if (file1.is_open())
后跟else
更容易阅读代码,以便在未打开时打印错误消息。
我为什么这么说?因为程序员对is_open
的意图比使用更广泛的术语fail
更容易辨别。
if (file1.is_open())
{
while (!file1.eof())
阅读Why is iostream::eof inside a loop condition considered wrong?,详细了解为什么这几乎总是错误的循环文件解决方案。
{
string size;
getline(file1, size);
始终测试读取以确保成功。此外,读取文件大小,然后不将其转换为整数,以便在后面的循环中使用。
for (int i = 0; i < 244; i++)
无论该文件中有多少条目,都将始终读取244。如果文件没有至少244个条目,则读取将失败,并且由于未检查读取是否成功,因此垃圾将存储在数组中。
另请注意,有一个循环遍历文件中的244个条目,这些条目被一个循环包围,该循环将尝试再次读取,直到设置了EOF标志。很可能你只想要一个这样的循环。
{
file1 >> ourPlayers[i].playerID;
file1 >> ourPlayers[i].name;
}
}
file1.close();
}
fullPlayerPointer = &ourPlayers[0];
指向先前进行的动态分配的指针被指向临时分配ourPlayers
的指针覆盖。对长期存储的引用已被对即将超出范围并变为无效的存储的引用所取代。
OP有可能打算将短期存储中的数据复制到长期存储中,但不幸的是,这并不是编译器被告知要做的事情,并且它并不值得这样做。将文件直接读入长期存储会更有用。
return fullPlayerPointer;
从函数返回并向调用者提供无效数组。 }
在那里修理很多。
这是一个非常简单的方法,可以解决上述所有问题,但会暴露出更多问题:
Player * fillPlayers()
{
ifstream file1("Players.txt");
Player * players = nullptr;
if (file1.is_open())
{
int size;
if (file1 >> size)
{
players = new Player[size];
int index = 0;
while (file1 >> players[index].playerID >> players[index].name
&& index < size)
{
}
// extra brains needed here to handle premature read failure.
}
file1.close();
}
else
{
cerr << "file1 did not open";
}
return players; // only a pointer to the array was returned. How big the
// array is and how many items are actually in it is lost
}
这是std::vector
真正变得非常棒的地方。它知道它有多大以及它有多饱满。数组没有。
现在,假设不允许使用std :: vector,并且Paul McKenzie已经covered what to do if it isn't,那么聪明的做法就是在数组周围做一个非常简单的包装,以获得一些安全性和易用性。 vector
提供。
class stupidvector
{
Player *players;
size_t capacity; // a typical vector implementation uses a pointer for
// this to make iteration easier
size_t size; // vector uses pointer here, too.
public:
stupidvector();
stupidvector(size_t size);
// correctly copy a stupid vector Rule of Three. In this case, don't
// allow it to be copied.
stupidvector(const stupidvector& src)=delete;
// correctly move a stupid vector Rule of Five
stupidvector(stupidvector && src);
// release storage
~stupidvector();
// add an item to the end
void push(const Player & player);
// how big is it?
size_t getcapacity();
// how full is it?
size_t getsize();
// make it act like an array
Player & operator[](size_t index);
// correctly assign. Rule of Three. again we don't want to copy these,
// but if we did, look at Copy and Swap Idiom for a neat solution
stupidvector& operator=(const stupidvector& src) = delete;
// correctly move
stupidvector& operator=(stupidvector && src);
};