私は、いくつかの特定の要件を持つ宿題に取り組んでいます。引数としてスコアの配列を取る TestScores という名前のクラスが必要です。いずれかのスコアが負または 100 より大きい場合、例外がスローされます。最後に、すべてのスコアの平均を返すメンバー関数が必要です。配列をコンストラクターに渡すだけの方法を見つけるほど頭が良くなかったので、配列のサイズを示す int も追加しました。
コードを実行すると (まだ例外をテストしていません)、Segmentation fault エラーが発生し続けます。Valgrind と gdb はかなり役に立たず、次のようなメッセージを出力します。
==9765== Jump to the invalid address stated on the next line
==9765== at 0x2200000017: ???
さらに不思議なことに (少なくとも私には)、クライアント コードの for ループで、インクリメンタ i が、TestScores オブジェクトを作成した直後に、0 から一見ランダムな 2 桁の数字に変化します。以前のバージョンでは、rand() を使用して配列を作成する前は、インクリメントせずに無限ループを実行していました。
TestScores.cpp の内容は次のとおりです。
#include <iostream>
using std::cout;
using std::endl;
#include "TestScores.h"
#include <stdexcept>
using std::runtime_error;
// Constructor.
TestScores::TestScores(int a[], int s):
_SIZE(s), _scores()
{
// Look at each item in a[], see if any of them are invalid numbers, and
// only if the number is ok do we populate _scores[] with the value.
for (int i = 0; i < _SIZE; ++i)
{
if (a[i] < 0)
{
throw runtime_error ("Negative Score");
}
else if (a[i] > 100)
{
throw runtime_error ("Excessive Score");
}
_scores[i] = a[i];
cout << _scores[i] << " ";
}
cout << endl;
}
// Finds the arithmetic mean of all the scores, using _size as the number of
// scores.
double TestScores::mean()
{
double total = 0;
for (int i = 0; i < _SIZE; ++i)
{
total += _scores[i];
}
return total / _SIZE;
}
// median() creates an array that orderes the test scores by value and then
// locates the middle value.
double TestScores::median()
{
// Copy the array so we can sort it while preserving the original.
int a[_SIZE];
for (int i = 0; i < _SIZE; ++i)
{
a[i] = _scores[i];
}
// Sort the array using selection sort.
for (int i = 0; i < _SIZE; ++i)
{
int min = a[i];
for (int j = i + 1; j < _SIZE; ++j)
{
if (a[j] < min)
{
min = a[j];
a[j] = a[i];
a[i] = min;
}
}
}
// Now that array is ordered, just pick one of the middle values.
return a[_SIZE / 2];
}
クライアントコードは次のとおりです。
#include <iostream>
#include "TestScores.h"
#include <stdexcept>
#include <cstdlib>
#include <ctime>
using std::exception;
using std::cout;
using std::endl;
int main()
{
const int NUM_STUDENTS = 20,
NUM_TESTS = 4;
int test [NUM_TESTS][NUM_STUDENTS];
// Make random seed to populate the arrays with data.
unsigned seed = time(0);
srand(seed);
// Populate the scores for the individual tests graded for the semester.
// These will all be values between 0 and 100.
for (int i = 0; i < NUM_TESTS; ++i)
{
for (int j = 0; j < NUM_STUDENTS; ++j)
{
test[i][j] = rand() % 100;
cout << test[i][j] << " ";
}
cout << endl;
}
// Now we have the data, find the mean and median results for each test.
// All values should be valid, but we'll handle exceptions here.
for (int i = 0; i < NUM_TESTS; ++i)
{
cout << "For Test #" << i + 1 << endl;
try
{
cout << "i = " << i << endl; // i = 0 here.
TestScores results(test[i], NUM_STUDENTS);
cout << "i = " << i << endl; // i = some random number here.
cout << "Mean: " << results.mean() << endl;
cout << "Median:" << results.median() << endl << endl;
}
catch (exception &e)
{
cout << "Error, invalid score: " << e.what() << endl;
}
cout << "For Test #" << i + 1 << endl;
}
return 0;
}
編集:ヘッダーも要求されました:
#ifndef TEST_SCORES_H
#define TEST_SCORES_H
class TestScores
{
private:
const int _SIZE;
int _scores[];
public:
// Constructor
TestScores(int a[], int);
double mean() const,
median() const;
};
#endif
私は配列を動的にすることをいじり、配列を空として初期化しなかったので、問題が解決したので、それが私が提出したものです.それは私にいくつかのフォローアップの質問につながります.
動的にする前に、既に初期化されているはずのサイズ値を配列 _scores に与えることで、配列 _scores の初期化をいじってみました。これにより、コンパイラの問題が発生しました。そのことについて先生に相談したところ、ハードワイヤードのグローバル定数がない限り、配列にスペースを割り当てることはできないとのことでした。つまり、コンストラクターでサイズ値を渡して配列を初期化することはできません。それは本当ですか、もしそうなら、それはなぜですか?
少し戻って、多くの値が必要な場合は動的配列の方が優れているように思えます。なぜなら、メモリ内に連続したスペースのブロックが必要ないからです。したがって、小さな配列を作成している場合、動的配列を作成するために入力するスペースと時間の無駄に思えます。これは真実ではありませんか?これからはすべての配列を動的にする必要がありますか? この経験により、少なくともクラスに関連する通常の配列の有用性に関する私の意見が確実に変わりました。
また、私は課題で完全な評価を得ましたが、サイズの引数を渡すことで精神に違反したように感じます (文字通りの問題ステートメントには、「クラス コンストラクターはテスト スコアの配列を引数として受け入れる必要があるため」)。ハードワイヤードのグローバル定数またはサイズ引数を持つ以外に、配列だけを渡す方法はありますか? これを行う方法を考えるのにかなりの時間を費やしたことを誓います。