2012-04-12 10 views
1

私はいくつかの特定の要件を満たして宿題に取り組んでいます。 TestScoresという名前のクラスがなければなりません.Questの配列を引数として取ります。スコアが負数であるか100より大きい場合は例外がスローされます。最後に、すべてのスコアの平均を返すメンバー関数が必要です。私は配列をコンストラクタに渡すしか方法が見つからないほど巧妙ではなかったので、配列のサイズを示すintも追加しました。セグメンテーションフォールトと不思議なループ動作

コードを実行すると(まだ例外がテストされていない)、セグメンテーションフォールトエラーが発生し続ける。 ValgrindのとGDBのようなメッセージを出力し、むしろ役に立たないされています(少なくとも私には)さらに不思議な

==9765== Jump to the invalid address stated on the next line 
==9765== at 0x2200000017: ??? 

を、クライアントコードでのためのループでは、私のインクリメンタは、私は、何とか0からぶつかっますtestScoresオブジェクトを作成した直後の、ランダムな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の初期化を行いました。これはコンパイラの問題を引き起こしました。私は教師と話をしましたが、配線されたグローバル定数がない限り、配列のスペースを割り当てることはできません。つまり、配列を初期化するためにコンストラクタにサイズ値を渡すことはできません。それは本当ですか、もしそうなら、なぜですか?

少し前に戻って、メモリに連続したスペースブロックを必要としないため、多くの値が必要な場合は動的配列が優れているようです。したがって、小さな配列を作成している場合は、動的配列を作成するためにスペースと時間の入力が浪費されるようです。これは間違っていますか?今からすべての配列を動的にするべきですか?この経験は、少なくともクラスに関係するものとして、通常の配列の有用性についての私の意見を確かに変えました。

また、私は割り当てについて完全な評価を得ていますが、大きさの引数を渡すことで精神に違反しているように感じます(「リテラル問題文は次のようになります:」クラスのコンストラクタは、 ")。固定配線されたグローバル定数の他に、サイズ引数を持っているだけで、配列だけを渡す方法はありますか?私は良い一時間を過ごして、これを行う方法を考えようと誓っています。

+3

'TestScores.h'も投稿できますか? – QuantumMechanic

+0

とにかくスコアは何ですか? – leftaroundabout

+2

gccを使用している場合は、すべてに "-g"と "-O0"フラグを追加し、valgrindをもう一度実行します。コード行番号が表示されることがあります。 – pzanoni

答えて

0

TestScores.hがなければ1「を推測する必要がありますが、あなたの_scoresメンバ変数にポイントが正しく初期化し、ときにされていないことを、あなたはTestScoresオブジェクトを作成しているループ内で破壊さiの価値について言うことを与えられましたそれをロードしようとすると、実際にはメモリを駄目にしています。

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


今すぐ更新しましたTestScores.hが利用可能です。

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

あなたのコンストラクタの最初の行は次のようになります。_SIZEint Sを保持するためのメモリを割り当て、そのメモリを指すように_scoresを設定します

_scores = new int[_SIZE]; 

_scores[i]への割り当ては、実際にあなたのプログラムに属する定義されたメモリに入ります。

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

delete [] _scores; 

これは_scoresポイントにするメモリのブロックを解放します。 delete操作でドキュメントを読むと、この場合に[]が存在する必要がある理由を確認できます。

+0

オリジナルの質問を更新してTestScores.hを表示しました。 – beriukay

0

_scoresは初期化されていないようです。コンストラクタの先頭に_scores = new int[s];が必要です(デストラクタのdelete[] s;)。

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

+0

これは '_scores'が' int * 'であると仮定しています。これはまだわかりません。しかし、はい、私はまた、これを引き起こしている '_scores'に初期化の問題があると仮定します。 – QuantumMechanic

+0

コンストラクタはスローすることができるので、デストラクタは常に実行されるとは限りません。 –

+0

はい、_scoresを動的配列にすると、すべての問題が解決されました。しかし、以前の.hを追加した後、問題をよりよく理解するためにフォローアップの質問がありました。 – beriukay