提问人:Emil Lang 提问时间:6/8/2022 更新时间:6/20/2022 访问量:684
在 C++ 中,如何使用作为对象数组的私有变量?
In C++, how do I use a private variable that is an array of objects?
问:
我正在尝试学习 C++ 并弄清楚如何访问作为对象数组的私有成员变量。我的目标是尝试打印出对象数组中的数据。假设我的标题如下所示。
using namespace std;
const unsigned MAX_RESULTS = 10;
class StudentRecords{
public:
StudentRecords();
//bunch of other getters and setters here
Result GetResults() const; //my lame attempt at trying to access the obj-array private var
private:
Result results[MAX_RESULTS]; // array of Result of MAX_RESULTS number of elements
//+other private variables
};
ostream& operator <<( ostream& os, const StudentRecords& R);
在上面,应该有一个称为 results 的 Result 对象的私有数组,其大小为 MAX_RESULTS,这里应该是 10。现在,使用我的重载运算符<<的想法是将 Result 的内容打印到“file”中,以便说话。由于它是一个数组,我想使用 for 循环打印出数组中的所有结果。
Result StudentRecords::GetResults() const
{
return results[MAX_RESULTS];
}
ostream & operator <<( ostream & os, const StudentRecords& R )
{
for(unsigned i = 0; i < SomeNumber; i++)
{
os << R.GetResults()[i] << '\n'; //this won't work of course see error
}
return os;
}
将出现错误,指出:
error: no match for 'operator[]' (operand types are 'Result' and 'unsigned int')|
我已经重载了 Result 类中的 << 运算符,以便打印出该类中的值。问题是我不知道如何遍历结果数组。从我谷歌上搜索的内容中,我知道您可以使用某种指针函数,例如这里: C++:数组的 Setters 和 Getters
当我尝试编写这样的函数时:
Result* GetResults() const;
我会收到一个错误,指出:
error: cannot convert 'const Result' to 'Result*' in return|
省略 * 允许代码编译,但可以预见的是,我从数组中得到了一堆垃圾值。因此,假设我的类有一个对象数组,并且这些对象有自己的变量,我如何从对象数组中打印出值?我很感激你的帮助。
答:
Result StudentRecords::GetResults() const
{
return results[MAX_RESULTS];
}
这是一个常见的新手误解。因为你声明了数组,因为你认为这在某种程度上意味着整个数组。但事实并非如此,当您使用数组时,数组用于访问数组的各个元素。当然,索引处没有元素,这已经过了数组的末尾。results[MAX_RESULTS]
results[MAX_RESULTS]
[]
MAX_RESULTS
底线是声明的语法和表达式的语法是不一样的。
你可以做这样的事情
const Result* StudentRecords::GetResults() const
{
return results;
}
这将返回指向数组的指针,而不是数组本身(这在 C++ 中实际上是不可能的)。但现在应该已经足够接近了。
正如许多人所指出的那样,在解决这个问题时需要考虑各种事情。
- 对象数组的 getter 可以采用索引号(良好的旧 int i)来定位数组内的元素。例如 R.GetResult(i)
- 声明不是表达!在我的 getter 中,我使用了表达式“return results[MAX_RESULTS];”,其中 MAX_RESULTS 是一个常量......但这最终意味着在第 MAX_RESULTS 个元素处返回数组,这显然不是故意的。
- 我将来要尝试的一件事是创建一个指向数组成员变量的指针函数。
- 有人建议快速了解向量,因为“结果结果[MAX_RESULTS]”的声明,用评论员的丰富多彩的话来说,是“C++和C的邪恶混合,使事情变得比他们需要的更复杂。
评论
GetResult
GetResult(i)
一种方法是使输出函数成为类的一部分,以便它可以直接访问数组。虽然不能从字面上成为班级的一部分,但您可以通过将其声明为朋友来使其在逻辑上成为班级的一部分。results
operator<<
class StudentRecords {
friend ostream& operator <<( ost& os, const StudentRecords& R);
// Rest of the class definition
};
ostream & operator <<(ostream & os, const StudentRecords& R)
{
for(unsigned i = 0; i < MAX_RESULTS; i++) {
os << R.results[i] << '\n';
}
return os;
}
或者,您可以通过定义一个打印数组的成员函数并调用该成员来避免友谊。operator<<
class StudentRecords {
public:
void print_to(std::ostream& os) const
{
for(unsigned i = 0; i < MAX_RESULTS; i++) {
os << results[i] << '\n';
}
}
// Rest of the class definition
};
ostream & operator <<(ostream & os, const StudentRecords& R)
{
R.print_to(os);
return os;
}
请注意,此方法适用于类之外的其他任何内容都不需要访问数组的情况。如果你需要对数组进行外部访问,你不妨修复你的 getter(就像在其他答案中一样)并使用该公共访问进行打印。results
另一种方法(并不总是一个好方法,但要记住一点)是保留返回数组的想法,但从 C 样式数组升级到 .A 的行为很像 C 样式的数组,但它不会衰减为指针。这样可以更轻松地将数组传入和传出函数。std::array
std::array
// In the class definition
const std::array<Result, MAX_RESULTS> & GetResults() const {
return results;
}
private:
std::array<Result, MAX_RESULTS> results;
返回引用可防止不必要的副本,而进行引用可防止外部代码更改数据。const
使用这种类型的返回值,表达式在流式处理运算符中变为有效。事实上,您的代码中可能只需要进行三次更改即可使用 ,并且这些更改都在上面的代码块中进行了演示。R.GetResults()[i]
std::array
- 更改 的类型。
results
- 更改 的返回类型。
GetResults()
- 只需返回即可。
GetResults()
results
(虽然我也内联了 的定义,但这只是为了方便起见,而不是修复的一部分。GetResults()
但是,我们仍然可以做得更好。顾名思义,数组中的可用结果可能少于最大值。使用 而不是 in 来支持此推论。对于在编译时不知道有用大小的数组,a 是更合适的解决方案。MAX_RESULTS
SomeNumber
MAX_RESULTS
operator<<
std::vector
与切换到 不同,切换到 将需要对一些未包含在问题中的代码进行更改。例如,可能有一种添加结果的方法。新方法不是跟踪已存储的结果数,将新结果分配给下一个可用位置,然后递增计数,而是针对新结果。一旦所有内容都转换好了,常量应该不被使用,因此可以消除(这是切换到向量的第一个好处)。std::array
std::vector
push_back()
MAX_RESULTS
对于问题中的代码,更改类似于切换到 时所做的更改。std::array
// In the class definition
const std::vector<Result> & GetResults() const {
return results;
}
private:
std::vector<Result> results;
与该方法一样,您的将按照您编写的方式工作,假设以某种方式设置为 .但是,您可以通过切换到基于范围的循环来更进一步实现。std::array
operator<<
SomeNumber
R.GetResults().size()
ostream & operator <<( ostream & os, const StudentRecords& R )
{
// Range-based looping removes the need to track indices.
for (const auto& result : R.GetResults())
{
os << result << '\n';
}
return os;
}
这些方法的缺点是“数组”或“向量”成为接口的一部分,而不是实现细节。这是对哪种设计更可取的判断。就个人而言,如果返回向量是可取的,我倾向于放置类似的东西
// Container used to store `Result` objects.
using ResultContainer = std::vector<Result>;
,然后将 的所有其他用法替换为 。这会告诉类的用户,他们可能会假设是一个容器,并尝试对它进行基于范围的循环,但这意味着不应假定特定于向量的功能。(影响很弱;更好的文档会更好。如果有理由,这使您可以灵活地更换为其他容器。std::vector<Result>
ResultContainer
ResultContainer
std::vector
评论
return results[MAX_RESULTS];
Result
MAX_RESULTS
results
Result
const Result* GetResults() const { return results; }
R.GetResults()[i]
R.GetResult(i)
<vector>
Result results[ MAX_RESULTS ]
std::array<>
MAX_RESULTS
<array>
<vector>
<array>