1

私は、いくつかの特定の要件を持つ宿題に取り組んでいます。引数としてスコアの配列を取る 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 の初期化をいじってみました。これにより、コンパイラの問題が発生しました。そのことについて先生に相談したところ、ハードワイヤードのグローバル定数がない限り、配列にスペースを割り当てることはできないとのことでした。つまり、コンストラクターでサイズ値を渡して配列を初期化することはできません。それは本当ですか、もしそうなら、それはなぜですか?

少し戻って、多くの値が必要な場合は動的配列の方が優れているように思えます。なぜなら、メモリ内に連続したスペースのブロックが必要ないからです。したがって、小さな配列を作成している場合、動的配列を作成するために入力するスペースと時間の無駄に思えます。これは真実ではありませんか?これからはすべての配列を動的にする必要がありますか? この経験により、少なくともクラスに関連する通常の配列の有用性に関する私の意見が確実に変わりました。

また、私は課題で完全な評価を得ましたが、サイズの引数を渡すことで精神に違反したように感じます (文字通りの問題ステートメントには、「クラス コンストラクターはテスト スコアの配列を引数として受け入れる必要があるため」)。ハードワイヤードのグローバル定数またはサイズ引数を持つ以外に、配列だけを渡す方法はありますか? これを行う方法を考えるのにかなりの時間を費やしたことを誓います。

4

2 に答える 2

0

まったく初期化していないようです_scores_scores = new int[s];コンストラクターの先頭 (およびdelete[] s;デストラクタ内) に必要です。

を初期化せず_scoresに、未定義のメモリ位置に書き込みます。

于 2012-04-12T16:24:49.177 に答える
0

推測する必要はありませんが、オブジェクトを作成しているループで破損しているTestScores.h値についてあなたが言うことを考えると、それはメンバー変数が適切に初期化されていないことを示しており、ロードしようとすると実際に記憶を捨てる。iTestScores_scores

表示されたらTestScores.h、ファイルを考慮してこの回答を再検討します。


利用できるようTestScores.hになりました。

問題は、初期化していないことです_scores。実際には、配列を保持するためにメモリを割り当てているわけではなく、ポインタをそのメモリを指すように設定していません。したがって、配列に物を格納しようとすると、どこかでメモリを破棄するだけです。

コンストラクターの最初の行は次のようになります。

_scores = new int[_SIZE];

_SIZE intこれにより、 sを保持するメモリが割り当てられ_scores、そのメモリを指すように設定されます。次に、への割り当て_scores[i]は、実際にはプログラムに属する定義済みのメモリに入ります。

もちろん、 のインスタンスがTestScore破棄されたときに、このメモリを解放する必要もあります (C++ は解放しません)。したがって、デストラクタを定義して実装する必要がありTestScores、そのデストラクタには次の行を含める必要があります。

delete [] _scores;

_scoresこれにより、指すメモリのブロックが解放されます。操作に関するドキュメントを読んで、この場合に必要なdelete理由を確認できます。[]

于 2012-04-12T16:22:13.813 に答える