在 C++ 中,如何使用作为对象数组的私有变量?

In C++, how do I use a private variable that is an array of objects?

提问人:Emil Lang 提问时间:6/8/2022 更新时间:6/20/2022 访问量:684

问:

我正在尝试学习 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|

省略 * 允许代码编译,但可以预见的是,我从数组中得到了一堆垃圾值。因此,假设我的类有一个对象数组,并且这些对象有自己的变量,我如何从对象数组中打印出值?我很感激你的帮助。

C++ 数组类 对象 private-members

评论

1赞 Nathan Pierson 6/8/2022
该行尝试返回包含数组索引的单个。由于不存在此类函数,因此即使此函数具有适当的返回类型,它也将是未定义的行为。如果你想返回一个指向数组开头的指针,也许?return results[MAX_RESULTS];ResultMAX_RESULTSresultsResultconst Result* GetResults() const { return results; }
1赞 JaMiT 6/8/2022
你尝试使用这个函数看起来像 -- 为什么不是更简单的东西呢?要键入的字符少了三个。R.GetResults()[i]R.GetResult(i)
1赞 DevSolar 6/8/2022
必在第一时间熟悉。 是 C++ 和 C 的邪恶混合,使事情变得比他们需要的更复杂。<vector>Result results[ MAX_RESULTS ]
1赞 DailyLearner 6/8/2022
使用代替普通数组。std::array<>
1赞 DevSolar 6/8/2022
@Aamir:使用 of 表示计数达到该数字,做出错误的选择。 首选容器。C 数组固有的几个问题延续到 ,因此它不是一般推荐的理想选择。MAX_RESULTS<array><vector><array>

答:

4赞 john 6/8/2022 #1
Result StudentRecords::GetResults() const
{
    return results[MAX_RESULTS];
}

这是一个常见的新手误解。因为你声明了数组,因为你认为这在某种程度上意味着整个数组。但事实并非如此,当您使用数组时,数组用于访问数组的各个元素。当然,索引处没有元素,这已经过了数组的末尾。results[MAX_RESULTS]results[MAX_RESULTS][]MAX_RESULTS

底线是声明的语法和表达式的语法是不一样的。

你可以做这样的事情

const Result* StudentRecords::GetResults() const
{
    return results;
}

这将返回指向数组的指针,而不是数组本身(这在 C++ 中实际上是不可能的)。但现在应该已经足够接近了。

0赞 Emil Lang 6/9/2022 #2

正如许多人所指出的那样,在解决这个问题时需要考虑各种事情。

  1. 对象数组的 getter 可以采用索引号(良好的旧 int i)来定位数组内的元素。例如 R.GetResult(i)
  2. 声明不是表达!在我的 getter 中,我使用了表达式“return results[MAX_RESULTS];”,其中 MAX_RESULTS 是一个常量......但这最终意味着在第 MAX_RESULTS 个元素处返回数组,这显然不是故意的。
  3. 我将来要尝试的一件事是创建一个指向数组成员变量的指针函数。
  4. 有人建议快速了解向量,因为“结果结果[MAX_RESULTS]”的声明,用评论员的丰富多彩的话来说,是“C++和C的邪恶混合,使事情变得比他们需要的更复杂。

评论

1赞 JaMiT 6/12/2022
请记住,您发布的答案是为了帮助其他人解决同样的问题。你的答案不应该只是你所读内容的肤浅的“读书报告”。我建议添加一个代码示例来演示您的解决方案。一旦你这样做了,你可能会发现,如果你的答案专注于一种方法,而不是试图总结一切,那么它更容易阅读。(删除 #2 特别容易,因为另一个答案涵盖了这一点。我建议关注 #1,解释为什么这是一个好方法(不必很长),并展示 .GetResult
0赞 Emil Lang 6/13/2022
我会牢记这一点!既然你已经输入了一个很好的答案,如果你不介意的话,我就勾选它吧?
1赞 JaMiT 6/13/2022
我仍然认为您应该将代码添加到您的答案中。有几种可能的方法,最好为每种方法提供示例。GetResult(i)
1赞 JaMiT 6/12/2022 #3

一种方法是使输出函数成为类的一部分,以便它可以直接访问数组。虽然不能从字面上成为班级的一部分,但您可以通过将其声明为朋友来使其在逻辑上成为班级的一部分。resultsoperator<<

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

0赞 JaMiT 6/20/2022 #4

另一种方法(并不总是一个好方法,但要记住一点)是保留返回数组的想法,但从 C 样式数组升级到 .A 的行为很像 C 样式的数组,但它不会衰减为指针。这样可以更轻松地将数组传入和传出函数。std::arraystd::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_RESULTSSomeNumberMAX_RESULTSoperator<<std::vector

与切换到 不同,切换到 将需要对一些未包含在问题中的代码进行更改。例如,可能有一种添加结果的方法。新方法不是跟踪已存储的结果数,将新结果分配给下一个可用位置,然后递增计数,而是针对新结果。一旦所有内容都转换好了,常量应该不被使用,因此可以消除(这是切换到向量的第一个好处)。std::arraystd::vectorpush_back()MAX_RESULTS

对于问题中的代码,更改类似于切换到 时所做的更改。std::array

  // In the class definition

  const std::vector<Result> & GetResults() const {
    return results;
  }

private:
  std::vector<Result> results;

与该方法一样,您的将按照您编写的方式工作,假设以某种方式设置为 .但是,您可以通过切换到基于范围的循环来更进一步实现。std::arrayoperator<<SomeNumberR.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>ResultContainerResultContainerstd::vector