2

レガシーコードでこのバグに出くわしました:

class MyAPIHandler
{
    private:
     int handle;
    public:
    void MyApiHandler()  // default constructor
    {
        handle = 42;
    };
};

警告なしで正常にコンパイルされますが、コンストラクター名のスペルが間違っているため、動作は意図したものではありませんでした。これだけで「関数は値を返さない」という警告が表示されますが、自動操縦を使用していて、これを「修正」するために「void」戻り値の型を追加したと思います。

さて、バグ修正は簡単でしたが、私の質問はこれです:-

この種のバグの再発を防ぐには、どのような手法を使用できますか?

一部の言語では、明示的な「コンストラクター」キーワードが必要であり、この問題が明らかになるはずです。単体テストでも、明らかにそれをキャッチする必要がありました。他に何ができますか?

4

13 に答える 13

19

コンストラクターで常に初期化リストを使用する場合:

MyApiHandler()  // default constructor
: handle(42)
{
}

コンストラクターのバグの名前が間違っている可能性はさらに低く、とにかくスタイルが優れています。

編集:リンクのコメンターに感謝

于 2008-12-01T11:14:24.047 に答える
2

コードレビュー?あなたが言及したように、ユニットテストも良いです。コード カバレッジ。バグを見つけるための通常のツールの多くは、これを見つけることができます。

于 2008-12-01T10:51:53.293 に答える
2

注意を払ったり、コードをレビューしたりする以外に、できることはあまりありませ

次のようなトピックを使用して、新しいクラスを作成するためのチェックリストを作成できます。

  • 最初のコンストラクターとして使用するクラス名をコピーして貼り付けます。
  • コピー コンストラクターを実装する場合は、デストラクタとコピー代入演算子 ( Rule of Three .
  • 単一引数のコンストラクターまたはデフォルトの引数を持つコンストラクターには注意してください - それらを明示的にすることを検討してください

これらはコンストラクター関連のポイントであり、チェックリスト全体が長くなる可能性があります。しばらくすると、このようなことを自動的に開始します。

個人的には、すでに述べたように、単体テストが特定の問題を回避するための最良の答えだと思います。

編集:コメントから追加されたアイデア:
一部の開発環境では、コード テンプレートまたはマクロを使用して正しいクラス スケルトンを生成できます。これは真の「プログラマーのソリューション」です。エラーを回避するために自動化できるものはすべて自動化します。

于 2008-12-01T10:55:30.307 に答える
2

これはあまり問題にならないはずです。コンストラクターには戻り値がない (void でさえない) ため、コンパイラーは何かがコンストラクターまたはメンバー関数であるかどうかを判断し、問題があるかどうかを通知できるはずです。明らかに、誰かがコンパイラのエラーを見て、間違った解決策を選択しました (スペルを修正する代わりに戻り値の型を追加しました)。(編集:申し訳ありませんが、自分でやったことに気づきませんでした。)

個人的には、私は常にコンストラクターをクラスの public セクションの先頭近く (メンバー関数の前、ただし public 型の定義と定数の後) に配置するため、そのような問題は、コード。しかし、慣習はそれについて異なります。

于 2008-12-01T18:13:20.393 に答える
1

この問題についてあまり考えるべきではないと思います

これはコンストラクターだけでなく、他のメソッドや関数でも発生します。例えば:

class MyBase
{
   // etc.
   virtual void doSomething() ;
} ;

class MyDerived : public MyBase
{
   // etc.
   virtual void DoSomething() ;
} ;

または:

bool isOk(int value) { /* etc. */ }
bool isOK(double value) { /* etc. */ }

void doSomething(int value)
{
   if(isOK(value)) // isOK(double) will be called
   {
      // Etc.
   }
}

そして、これは大文字と小文字の問題だけではありません。この種のエラーが発生します。オートコンプリートのようなIDEヘルパーは、優れた単体テストコード(クラスのすべてのメソッドをカバーするもの)と同様にある程度役立ちますが、追加のキーワードを使用しても、コンパイラだけでこの種の開発者のタイプミスを防ぐことができるとは思いません。

コンストラクターはどうですか?

私の前に述べたコンストラクターの定義に関しては、これは悪い考えです。

コメントと同じくらい役に立ちます(したがって、代わりに/ * CONSTRUCTOR * /を使用しないのはなぜですか?)。誰かがCONSTRUCTORという単語を定義記号として使用することを考えた場合、他の誰かが同じ考えを持っていることは間違いありません。あなたが含めているが所有していないいくつかのヘッダー、そしてそれはあなたのコードを壊すでしょう...

于 2008-12-01T18:38:34.740 に答える
1

メンバー変数が変更されない場合は、それを const として宣言すると、コンパイラは初期化リストを使用するように強制します。この場合、エラーを検出する必要があります (コンパイルされないため)。

于 2009-03-17T05:46:13.353 に答える
1

静的コード分析ツールである PC Lint がこのエラーを発見したことは間違いありません。無料ではありませんが、非常に優れています。一見の価値あり:

http://www.gimpel.com/

于 2008-12-01T10:58:59.833 に答える
0

ユニットテスト。

MyAPIHandler mah;
BOOST_CHECK_EQUAL(mah.handle, 42);
于 2008-12-01T13:56:55.120 に答える
0

私はこの種のタイプミスを回避するブロイラープレート/テンプレートのセットを持っています(Vimで..しかし、現代のエディターでそれらを使用できると思います)。

于 2008-12-01T18:45:44.460 に答える
0

この問題を回避する最も簡単な方法は、命名規則をコーディング スタイル ガイドの形で定めることだと思います。これで、エンティティに名前を付ける方法を設定し、それに固執します。

特に職場では、略語は最初の文字のみを大文字にすることが義務付けられているため、これはより簡単に検出されます。

于 2009-01-02T16:09:38.143 に答える
0

多分あなたはできる

#define CONSTRUCTOR

そして、あなたのコードで

class MyAPIHandler
{
   public:
       CONSTRUCTOR MyAPIHandler()
       {
           // Deep magic
       }
};

さて、これだけではどうにもなりませんが、このように書くことに慣れれば、間違いをより簡単に見つけることができるはずです。

正直なところ、これは非常にまれな出来事であり、手間をかける価値はないと思います.

于 2008-12-01T10:54:24.210 に答える
0

ほとんどの IDE では、何らかのマクロを使用できます。マクロを使用してクラス定義を作成するだけです。それが私がセットアップした方法です。私のマクロは次のように出力します:

class CHANGETHIS
{
    public: 
      CHANGETHIS();
      ~CHANGETHIS();
}
#error "Finish this class definition"

#error 行は、作業の途中で中断され、戻ったときに忘れてしまった場合に構築されないようにするための単なるセーフティ ネットです。

これについて私が気に入っているのは、どのエディターでも移植できることです。そのため、別の IDE に切り替えても障害を感じることはありません。お役に立てれば。

さらに、初期化リストを使用します。それらはより高速で、バグが少なくなります。

于 2008-12-01T20:56:12.857 に答える
0

あなたが探していた解決策ではありませんが、次のTDDが役立つと思います...

あなたがそのレガシーコードを言ったように、「レガシーコードでの作業 - ケント・ベック」を読むことをお勧めします。それは役立つかもしれません。

回帰テストでは、バグ修正による問題が検出されます。

私もすぐに使える提案を楽しみにしています:)

于 2008-12-01T11:01:21.090 に答える