web-dev-qa-db-ja.com

例外を作成する方法は?

だから私は、宿題のほとんどが中心になっている現在のアドレス帳プログラムで例外を扱い、それらを使用する今後の割り当てを持っています。私は例外を試し、全体をキャッチして、クラスデザインを使用することにしました。これは、数週間のうちに自分の課題のためにやらなければならないことです。私は例外をうまくチェックする作業コードを持っていますが、私が知りたいのは、エラーメッセージ関数を標準化する方法があるかどうかです(つまり、what()呼び出し):

ここに私のコードがあります:

#include <iostream>
#include <exception>
using namespace std;


class testException: public exception
{
public:
  virtual const char* what() const throw() // my call to the std exception class function (doesn't nessasarily have to be virtual).
  {
  return "You can't divide by zero! Error code number 0, restarting the calculator..."; // my error message
  }

  void noZero();

}myex;  //<-this is just a lazy way to create an object



int main()
{
void noZero();
int a, b;

cout << endl;

cout << "Enter a number to be divided " << endl;

cout << endl;

cin >> a;

cout << endl;

cout << "You entered " << a << " , Now give me a number to divide by " << endl;

cin >> b;

try
{    
    myex.noZero(b); // trys my exception from my class to see if there is an issue
}
catch(testException &te) // if the error is true, then this calls up the eror message and restarts the progrm from the start.
{
    cout << te.what() << endl;
    return main();
}

cout <<endl;

cout << "The two numbers divided are " << (a / b) << endl;  // if no errors are found, then the calculation is performed and the program exits.

return 0;

}

  void testException::noZero(int &b) //my function that tests what I want to check
  { 
    if(b == 0 ) throw myex;  // only need to see if the problem exists, if it does, I throw my exception object, if it doesn't I just move onto the regular code.
  }

私ができるようにしたいのは、どのタイプのエラーが呼び出されているかに応じて、what()関数が値を返すことができるようにすることです。したがって、たとえば、一番上の数字に見えるエラーを呼び出した場合、(a)ゼロであるかどうかを確認し、ゼロである場合は、「できない」というメッセージを設定します。ゼロの分子」が、それでもwhat()関数内にあります。次に例を示します。

  virtual const char* what() const throw() 
  if(myex == 1)
  {
      return "You can't have a 0 for the numerator! Error code # 1 "
  }
  else

  return "You can't divide by zero! Error code number 0, restarting the calculator..."; // my error message
  }

これは明らかに機能しませんが、エラーメッセージごとに異なる関数を記述しないようにする方法はありますか?

20
Charlie M

コードには多くの誤解が含まれています。簡単な答えはイエスです。あなたが望むものを返すためにwhat()を変更できます。しかし、一歩一歩行きましょう。

_#include <iostream>
#include <exception>
#include <stdexcept>
#include <sstream>
using namespace std;


class DivideByZeroException: public runtime_error {
public:

  DivideByZeroException(int x, int y)
    : runtime_error( "division by zero" ), numerator( x ), denominator( y )
    {}

  virtual const char* what() const throw()
  {
    cnvt.str( "" );

    cnvt << runtime_error::what() << ": " << getNumerator()
         << " / " << getDenominator();

    return cnvt.str().c_str();
  }

  int getNumerator() const
    { return numerator; }

  int getDenominator() const
    { return denominator; }

  template<typename T>
  static T divide(const T& n1, const T& n2)
    {
        if ( n2 == T( 0 ) ) {
            throw DivideByZeroException( n1, n2 );
        } 

        return ( n1 / n2 );
    }

private:
    int numerator;
    int denominator;

    static ostringstream cnvt;
};

ostringstream DivideByZeroException::cnvt;
_

そもそも、exceptionから派生した_runtime_error_は、派生元のアドバイスされた例外クラスです。これは、stdexceptヘッダーで宣言されています。 what()メソッドで返すメッセージでコンストラクタを初期化するだけです。

次に、クラスに適切な名前を付ける必要があります。これは単なるテストであると理解していますが、わかりやすい名前を付けるとコードを読んで理解するのに役立ちます。

ご覧のとおり、例外を引き起こした数値を分割するためにコンストラクタを変更しました。あなたは例外でテストをしました...まあ、私はこれを尊重しましたが、外部から呼び出すことができる静的関数として。

そして最後に、what()メソッド。 2つの数値を分割しているため、例外を引き起こした2つの数値を示すのは良いことです。それを達成する唯一の方法はostringstreamの使用です。ここでは静的にしますので、スタックオブジェクトへのポインタを返す問題はありません(つまり、cnvtをローカル変数にすると、未定義の動作が発生します)。

プログラムの残りの部分は、質問でリストしたとおりです。

_int main()
{
int a, b, result;

cout << endl;

cout << "Enter a number to be divided " << endl;

cout << endl;

cin >> a;

cout << endl;

cout << "You entered " << a << " , Now give me a number to divide by " << endl;

cin >> b;

try
{    
        result = DivideByZeroException::divide( a, b );

    cout << "\nThe two numbers divided are " << result << endl;
}
catch(const DivideByZeroException &e)
{
    cout << e.what() << endl;
}

return 0;

}
_

ご覧のとおり、return main()命令を削除しました。 main()を再帰的に呼び出すことはできないため、意味がありません。また、その目的は間違いです。例外を引き起こした操作を再試行することを期待しますが、例外はリエントラントではないため、これは不可能です。ただし、ソースコードを少し変更して、同じ効果を得ることができます。

_int main()
{
int a, b, result;
bool error;

do  {
    error = false;

    cout << endl;

    cout << "Enter a number to be divided " << endl;

    cout << endl;

    cin >> a;

    cout << endl;

    cout << "You entered " << a << " , Now give me a number to divide by " << endl;

    cin >> b;

    try
    {    
        result = DivideByZeroException::divide( a, b ); // trys my exception from my class to see if there is an issue

        cout << "\nThe two numbers divided are " << result << endl;
    }
    catch(const DivideByZeroException &e) // if the error is true, then this calls up the eror message and restarts the progrm from the start.
    {
        cout << e.what() << endl;
        error = true;
    }
} while( error );

return 0;

}
_

ご覧のとおり、エラーの場合、「適切な」部門が入力されるまで実行が続きます。

お役に立てれば。

37
Baltasarq

このような長さエラーの独自の例外クラスを作成できます

class MyException : public std::length_error{
public:
  MyException(const int &n):std::length_error(to_string(n)){}
};
3
class zeroNumerator: public std::exception
{
    const char* what() const throw() { return "Numerator can't be 0.\n"; }
};

//...

try
{
  myex.noZero(b); // trys my exception from my class to see if there is an issue
  if(myex==1)
  {
     throw zeroNumerator(); // This would be a class that you create saying that you can't have 0 on the numerator
  }

}
catch(testException &te) 
{
   cout << te.what() << endl;
   return main();
}

常にstd :: exception&eを使用する必要があります。そうする

catch(std::exception & e)
{
  cout<<e.what();
 }
2
John Kemp

クラスの階層を考慮する必要があります。

文字列を転送するためだけに例外を使用しようとすると、その理由は明らかではないかもしれませんが、例外を使用する実際の意図は、例外的な状況の高度な処理のためのメカニズムでなければなりません。 「throw」から対応する「catch」に移動するときに呼び出しスタックが巻き戻される一方で、C++ランタイム環境の内部で多くのことが行われています。

クラスの例は次のとおりです。

_class DivisionError : public std::exception {
public:
    DevisionError(const int numerator, const int divider)
        :numerator(numerator)
        , divider(divider)
    {
    }
    virtual const char* what() const noexcept {
        // ...
    }
    int GetNumerator() const { return numerator; }
    int GetDevider() const { return divider; }
private:
    const int numerator;
    const int divider;
};


class BadNumeratorError : public DivisionError {
public:
    BadNumeratorError(int numerator, int divider)
        : DivisionError(numerator, divider) 
    {
    }
    virtual const char* what() const noexcept {
    {
        // ...
    }
};


class ZeroDeviderError : public DivisionError {
public:
    ZeroDeviderError(int numerator, int divider)
        : DivisionError(numerator, divider)
    {
    }
    virtual const char* what() const noexcept {
    {
        // ....
    }
};
_
  • エラーにさまざまなクラスを提供することで、開発者はさまざまなエラーを特定の方法で処理することができます(エラーメッセージを表示するだけではありません)
  • エラーの種類の基本クラスを提供することにより、開発者はより柔軟になり、必要に応じて具体的になることができます。

場合によっては、具体的である必要があります

_} catch (const ZeroDividerError & ex) {
// ...
} catch (const DivisionError & ex) {
_

他では、しないでください

_} catch (const DivisionError & ex) {
_

追加の詳細については、

  • あなたがしたように投げる前に、あなたはあなたの例外のオブジェクトを作成するべきではありません。あなたの意図に関係なく、それはただ役に立たない-とにかく、あなたはcatchセクションでオブジェクトのコピーで作業しています(参照によるアクセスと混同しないでください)
  • Const参照を使用することは、非定数オブジェクトが本当に必要でない限り、良いスタイルcatch (const testException &te)になります。
1