We are trying to return an array of structures to print the contents in the main. When debugging the code, we get to the line right before the return statement and it shows that it is holding the right contents which is an int
and then a string
(playerID
and name
). As soon as the return statement executes the array is returned to the main
but only the playerID
is held in the array. All of the values for name
have been lost. Can someone explain why this would happen and a possible solution? If further clarification is needed please let me know.
我们正在尝试返回一个结构数组来打印main中的内容。在调试代码时,我们到了return语句之前的行,它显示它正在保存正确的内容,这是一个int,然后是一个字符串(playerID和name)。一旦return语句执行,数组就会返回到main,但只有playerID保存在数组中。名称的所有值都已丢失。有人可以解释为什么会发生这种情况并提供可能的解如果需要进一步说明,请告诉我。
#include<iostream>
#include<fstream>
#include<cstring>
#include<string>
#include<math.h>
using namespace std;
struct Player
{
int playerID;
string name;
};
Player *fillPlayers();
int main()
{
Player *myPlayerPointer;
myPlayerPointer = fillPlayers();
return 0;
}
Player * fillPlayers() {
ifstream file1;
Player * fullPlayerPointer = new Player[244];
Player ourPlayers[244];
file1.open("Players.txt");
if (file1.fail()) {
cerr << "file1 did not open";
}
if (file1.is_open()){
while (!file1.eof()){
string size;
getline(file1, size);
for(int i = 0; i < 244; i++){
file1 >> ourPlayers[i].playerID;
file1 >> ourPlayers[i].name;
}
}
file1.close();
}
fullPlayerPointer = &ourPlayers[0];
return fullPlayerPointer;
}
2 个解决方案
#1
4
This code looks a lot like C code. In C++ we have fancy RAII containers like std::vector
and std::array
that will do exactly what you want.
这段代码看起来很像C代码。在C ++中,我们有很多花哨的RAII容器,比如std :: vector和std :: array,它们可以完全按照你的意愿行事。
As for the issue, you are not returning an array, instead you are returning a pointer to an int
. You should check out What is array decaying?.
至于问题,你没有返回一个数组,而是返回一个指向int的指针。你应该看看什么是阵列衰变?
http://en.cppreference.com/w/cpp/container/vector
http://en.cppreference.com/w/cpp/container/array (C++ >= 11)
http://en.cppreference.com/w/cpp/container/vector http://en.cppreference.com/w/cpp/container/array(C ++> = 11)
#2
0
The generally correct answer is use a std::vector
as it assists in managing ownership and the lifetime of storage as well as tracking the capacity and resizing itself as needed. But let's take a walk through fillPlayers
to explain what is going wrong.
通常正确的答案是使用std :: vector,因为它有助于管理所有权和存储的生命周期,以及跟踪容量并根据需要调整自身大小。但是,让我们来看看fillPlayers来解释出了什么问题。
Player * fillPlayers()
{
ifstream file1;
Player * fullPlayerPointer = new Player[244];
Dynamically allocated 244 Player
s. Dynamic storage has manually controlled lifetime and will remain valid until such time as it is freed. Unfortunately this allocated storage is never used and not put away correctly later with delete[]
This allocation will be "leaked". As dire as this sounds, this is the "right" way to allocate the storage, but as we will see, the function uses it incorrectly.
动态分配244个玩家。动态存储具有手动控制的生命周期,并且在释放之前保持有效。不幸的是,这个分配的存储永远不会被使用,并且以后不能正确删除[]这个分配将被“泄露”。听起来很可怕,这是分配存储的“正确”方式,但正如我们将看到的,该功能错误地使用了它。
Player ourPlayers[244];
Statically allocated 244 Player
s as a temporary variable. This variable will only exist inside the innermost set of surrounding curly braces {}
. Afterward it will be rendered invalid. This means that references to ourPlayers
should not be returned from this function as ourPlayers
will be rendered invalid at the return from the function and before the caller can make use of it.
静态分配244个玩家作为临时变量。此变量仅存在于最内层的大括号{}内。之后它将被视为无效。这意味着不应该从this函数返回对thePlayers的引用,因为在函数返回时以及调用者可以使用它之前,myPlayers将变为无效。
file1.open("Players.txt");
if (file1.fail())
{
cerr << "file1 did not open";
}
Always test before using, so the above is almost right. It is rendered redundant by the next line performing a nearly identical test. This could be a good place to put an else
, but the code is more easily read with if (file1.is_open())
followed by an else
to print the error message if it is not open.
使用前一定要测试,所以上面几乎是正确的。执行几乎相同的测试的下一行使其变得多余。这可能是放置else的好地方,但是如果(file1.is_open())后跟一个else来打印错误消息(如果它没有打开),则更容易读取代码。
Why do I say this? Because the programmer's intent with is_open
is much easier to discern than with the broader term fail
.
我为什么这么说?因为程序员使用is_open的意图比使用更广泛的术语更容易辨别。
if (file1.is_open())
{
while (!file1.eof())
Read Why is iostream::eof inside a loop condition considered wrong? for details on why this is almost always the wrong solution to looping through a file.
读取为什么循环条件中的iostream :: eof被认为是错误的?有关为什么这几乎总是错误的循环文件解决方案的详细信息。
{
string size;
getline(file1, size);
Always test a read to ensure it succeeded. In addition, the file size was read and then not converted into an integer to use in the loop that follows.
始终测试读取以确保成功。此外,读取文件大小,然后不将其转换为整数,以便在后面的循环中使用。
for (int i = 0; i < 244; i++)
Regardless of how many entries there are in this file 244 will always be read. If the file does not have at least 244 entries, the read will fail and as the reads are not being checked for success, garbage will be stored in the array.
无论在该文件中有多少条目,都将始终读取244。如果文件没有至少244个条目,则读取将失败,并且由于未检查读取是否成功,因此垃圾将存储在阵列中。
Also note that there is a loop iterating through 244 entries in the file that is surrounded by a loop that will attempt to read again until the EOF flag is set. Most likely you only want one such loop.
还要注意,有一个循环遍历文件中的244个条目,这个条目被一个循环包围,该循环将尝试再次读取,直到设置了EOF标志。很可能你只想要一个这样的循环。
{
file1 >> ourPlayers[i].playerID;
file1 >> ourPlayers[i].name;
}
}
file1.close();
}
fullPlayerPointer = &ourPlayers[0];
The pointer to the dynamic allocation made earlier is overwritten by a pointer to the temporary allocation ourPlayers
. A reference to long-term storage has been replaced by a reference to storage that is about to go out of scope and become invalid.
前面所做的动态分配的指针被指向临时分配myPlayers的指针覆盖。对长期存储的引用已被对存储的引用所取代,该存储即将超出范围并变为无效。
It is possible that OP intended this to copy the data in the short term storage to the long term storage, but unfortunately that's not what the compiler was told to do, and it's not really worth doing. It would be much more useful to directly read the file into the long term storage.
OP有可能打算将短期存储中的数据复制到长期存储中,但不幸的是,这不是编译器被告知要做的事情,并且它并不值得做。将文件直接读入长期存储会更有用。
return fullPlayerPointer;
Returns from the function and gives an invalid array to the caller. }
从函数返回并向调用者提供无效数组。 }
Lot to fix in there.
很多在那里修复。
Here's a very simple approach that fixes all of the above problems but exposes a few more:
这是一个非常简单的方法,可以修复上述所有问题,但会暴露出更多:
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
}
This is where std::vector
really becomes awesome. It knows how big it is and how full it is. An array doesn't.
这就是std :: vector真的变得很棒的地方。它知道它有多大以及它有多饱满。数组没有。
Now, assuming std::vector is not allowed, and Paul McKenzie has already covered what to do if it isn't, the smart thing to do is make a very simple wrapper around the array to get some modicum of the safety and ease of use vector
provides.
现在,假设不允许使用std :: vector,并且Paul McKenzie已经介绍了如果不这样做,那么聪明的做法是在数组周围做一个非常简单的包装,以获得一些安全性和易用性。使用矢量提供。
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);
};
Pay special attention to the Rules of Three and Five
特别注意“三五规则”
#1
4
This code looks a lot like C code. In C++ we have fancy RAII containers like std::vector
and std::array
that will do exactly what you want.
这段代码看起来很像C代码。在C ++中,我们有很多花哨的RAII容器,比如std :: vector和std :: array,它们可以完全按照你的意愿行事。
As for the issue, you are not returning an array, instead you are returning a pointer to an int
. You should check out What is array decaying?.
至于问题,你没有返回一个数组,而是返回一个指向int的指针。你应该看看什么是阵列衰变?
http://en.cppreference.com/w/cpp/container/vector
http://en.cppreference.com/w/cpp/container/array (C++ >= 11)
http://en.cppreference.com/w/cpp/container/vector http://en.cppreference.com/w/cpp/container/array(C ++> = 11)
#2
0
The generally correct answer is use a std::vector
as it assists in managing ownership and the lifetime of storage as well as tracking the capacity and resizing itself as needed. But let's take a walk through fillPlayers
to explain what is going wrong.
通常正确的答案是使用std :: vector,因为它有助于管理所有权和存储的生命周期,以及跟踪容量并根据需要调整自身大小。但是,让我们来看看fillPlayers来解释出了什么问题。
Player * fillPlayers()
{
ifstream file1;
Player * fullPlayerPointer = new Player[244];
Dynamically allocated 244 Player
s. Dynamic storage has manually controlled lifetime and will remain valid until such time as it is freed. Unfortunately this allocated storage is never used and not put away correctly later with delete[]
This allocation will be "leaked". As dire as this sounds, this is the "right" way to allocate the storage, but as we will see, the function uses it incorrectly.
动态分配244个玩家。动态存储具有手动控制的生命周期,并且在释放之前保持有效。不幸的是,这个分配的存储永远不会被使用,并且以后不能正确删除[]这个分配将被“泄露”。听起来很可怕,这是分配存储的“正确”方式,但正如我们将看到的,该功能错误地使用了它。
Player ourPlayers[244];
Statically allocated 244 Player
s as a temporary variable. This variable will only exist inside the innermost set of surrounding curly braces {}
. Afterward it will be rendered invalid. This means that references to ourPlayers
should not be returned from this function as ourPlayers
will be rendered invalid at the return from the function and before the caller can make use of it.
静态分配244个玩家作为临时变量。此变量仅存在于最内层的大括号{}内。之后它将被视为无效。这意味着不应该从this函数返回对thePlayers的引用,因为在函数返回时以及调用者可以使用它之前,myPlayers将变为无效。
file1.open("Players.txt");
if (file1.fail())
{
cerr << "file1 did not open";
}
Always test before using, so the above is almost right. It is rendered redundant by the next line performing a nearly identical test. This could be a good place to put an else
, but the code is more easily read with if (file1.is_open())
followed by an else
to print the error message if it is not open.
使用前一定要测试,所以上面几乎是正确的。执行几乎相同的测试的下一行使其变得多余。这可能是放置else的好地方,但是如果(file1.is_open())后跟一个else来打印错误消息(如果它没有打开),则更容易读取代码。
Why do I say this? Because the programmer's intent with is_open
is much easier to discern than with the broader term fail
.
我为什么这么说?因为程序员使用is_open的意图比使用更广泛的术语更容易辨别。
if (file1.is_open())
{
while (!file1.eof())
Read Why is iostream::eof inside a loop condition considered wrong? for details on why this is almost always the wrong solution to looping through a file.
读取为什么循环条件中的iostream :: eof被认为是错误的?有关为什么这几乎总是错误的循环文件解决方案的详细信息。
{
string size;
getline(file1, size);
Always test a read to ensure it succeeded. In addition, the file size was read and then not converted into an integer to use in the loop that follows.
始终测试读取以确保成功。此外,读取文件大小,然后不将其转换为整数,以便在后面的循环中使用。
for (int i = 0; i < 244; i++)
Regardless of how many entries there are in this file 244 will always be read. If the file does not have at least 244 entries, the read will fail and as the reads are not being checked for success, garbage will be stored in the array.
无论在该文件中有多少条目,都将始终读取244。如果文件没有至少244个条目,则读取将失败,并且由于未检查读取是否成功,因此垃圾将存储在阵列中。
Also note that there is a loop iterating through 244 entries in the file that is surrounded by a loop that will attempt to read again until the EOF flag is set. Most likely you only want one such loop.
还要注意,有一个循环遍历文件中的244个条目,这个条目被一个循环包围,该循环将尝试再次读取,直到设置了EOF标志。很可能你只想要一个这样的循环。
{
file1 >> ourPlayers[i].playerID;
file1 >> ourPlayers[i].name;
}
}
file1.close();
}
fullPlayerPointer = &ourPlayers[0];
The pointer to the dynamic allocation made earlier is overwritten by a pointer to the temporary allocation ourPlayers
. A reference to long-term storage has been replaced by a reference to storage that is about to go out of scope and become invalid.
前面所做的动态分配的指针被指向临时分配myPlayers的指针覆盖。对长期存储的引用已被对存储的引用所取代,该存储即将超出范围并变为无效。
It is possible that OP intended this to copy the data in the short term storage to the long term storage, but unfortunately that's not what the compiler was told to do, and it's not really worth doing. It would be much more useful to directly read the file into the long term storage.
OP有可能打算将短期存储中的数据复制到长期存储中,但不幸的是,这不是编译器被告知要做的事情,并且它并不值得做。将文件直接读入长期存储会更有用。
return fullPlayerPointer;
Returns from the function and gives an invalid array to the caller. }
从函数返回并向调用者提供无效数组。 }
Lot to fix in there.
很多在那里修复。
Here's a very simple approach that fixes all of the above problems but exposes a few more:
这是一个非常简单的方法,可以修复上述所有问题,但会暴露出更多:
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
}
This is where std::vector
really becomes awesome. It knows how big it is and how full it is. An array doesn't.
这就是std :: vector真的变得很棒的地方。它知道它有多大以及它有多饱满。数组没有。
Now, assuming std::vector is not allowed, and Paul McKenzie has already covered what to do if it isn't, the smart thing to do is make a very simple wrapper around the array to get some modicum of the safety and ease of use vector
provides.
现在,假设不允许使用std :: vector,并且Paul McKenzie已经介绍了如果不这样做,那么聪明的做法是在数组周围做一个非常简单的包装,以获得一些安全性和易用性。使用矢量提供。
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);
};
Pay special attention to the Rules of Three and Five
特别注意“三五规则”