C++の初心者が犯す10のプログラミングミス

上の「 C言語とCPPプログラミング」をクリックし、「フォロー/トップ/スター」を選択します

公众号”干货福利,第一时间送达!

出典:https://blog.csdn.net/chenlycly

毎年一定の人員がいるため、それを補うために新入生を採用することもあり、新入生の指導は年配の社員の必修科目となっています。平日は、コードの問題をトラブルシューティングするために初心者を支援することがよくあります。その過程で、C++初心者が犯しがちなプログラミングミスを発見しました。これは初心者向けのリファレンスを提供する簡単な要約です。

1.いくつかのキーワードはcppファイルにもっと書かれています

C ++クラスの場合、仮想、静的、その他のキーワードなど、一部のキーワードは.hで記述する必要があり、cppで追加する必要はありません。さらにcppで記述すると、コンパイラはエラーを報告します。たとえば、ヘッダーファイルで宣言されている限り、次の仮想インターフェイスと静的メンバー変数の定義。

class shape
{
    virtual Draw();
    //...
    static int nLevel;
}

2.関数パラメーターのデフォルト値が関数実装に書き込まれます

パラメータのデフォルト値を持つ関数の場合、デフォルト値は関数宣言で追加され、関数実装でのパラメータを追加する必要はありません。コードを表示しやすくするために、デフォルト値は関数実装のパラメーターに注釈が付けられています。正しいことは、ヘッダーファイルにデフォルト値があることです:

BOOL CreateConf( const CString& strConfName, const BOOL bAudio = FALSE );
在函数实现处的参数中不用添加默认值:
BOOL CreateConf( const CString& strConfName, const BOOL bAudio/* = FALSE*/ );
{
    // ......
}

3.クラスを作成するときは、クラスの最後に「;」セミコロンを追加するのを忘れてください。

クラスの最後にセミコロンを追加するのを忘れると、コンパイルでエラーが報告され、初心者はコンパイルエラーの原因を見つけることなく長時間検索する可能性があります。実際には非常に単純で、クラスの最後にセミコロンを追加するのを忘れていました。

class Shape
{
    // ...
};

4.関数宣言のみが追加され、関数の実装は追加されません

クラスの関数を追加する場合、クラスのヘッダーファイルには関数宣言のみが追加されますが、cppには関数の実装は追加されません。この関数が他の場所で呼び出されunresolved external symbolた場合、コンパイルおよびリンク時にエラーが報告されます。実装がないため、リンクに使用できるobjファイルはありません。

5. cppファイルをプロジェクトに追加するのを忘れたため、リンク用のobjファイルが生成されません。

C ++クラスを追加するときは、通常、.hヘッダーファイルと.cppソースファイルを追加します。その結果、プロジェクトに.cppファイルを追加するのを忘れました。つまり、コンパイルに参加せず、リンク用のobjファイルを生成しませんでした。unresolved external symbolC ++クラスのインターフェイスを呼び出すコードがある場合、コンパイルおよびリンク時にエラーが報告されます。つまり、C++クラスに対応するインターフェイスはリンクできません。

6.この関数は、ローカル変数のアドレスまたは参照を返します

関数では、ローカル変数のアドレスまたは参照が返され、関数が終了するとこのローカル変数のライフサイクルが終了し、メモリが解放されます。変数のメモリが外部からアクセスされると、変数のメモリが解放されたため、メモリアクセス違反の例外がトリガーされます。たとえば、次のエラーコード:

char* GetResult()
{
    char chResult[100] = { 0 };

    // ......

    return chResult;
}

7.親クラスでインターフェイスの仮想関数を宣言するのを忘れたため、ポリモーフィズムが有効になりません

コードは元々、C ++ポリモーフィック仮想関数呼び出しを使用して、サブクラスによって実装されたインターフェイスを呼び出す必要がありました。その結果、対応するインターフェイスを親クラスで仮想として宣言するのを忘れたため、によって実装された関数を呼び出すことができませんでした。サブクラス。ポリモーフィック関数呼び出しを実装するには、親クラスの関連するインターフェースを仮想として宣言する必要があることに注意してください。

class Shape()
{
    // ...

    virtual void Draw();

    // ...
}

8.ダブルポインタを使用する必要がある場合は、シングルポインタを使用します

データを取得するためにインターフェイスを呼び出す必要がある場合があります。インターフェイスは、入力パラメータに対応するメモリにデータをコピーします。この場合、パラメータを設計するときにポインタまたは参照が渡されます。GetDataを呼び出す前に構造体ポインタpを定義し、対応する構造体オブジェクトメモリを新しく作成します。GetDataインターフェイスを定義するときにダブルポインタ(ポインタのポインタ)を使用する必要があり、結果が誤ってシングルポインタとして書き込まれます。

問題のコードは次のとおりです。

struct CodecInfo     // 编码信息
{
    int nFrameRate;

    // ...
}


CodecInfo* pInfo = new CodecInfo;

GetAudioCodecPtr()->GetCodecInfo(pInfo);   // 调用AudioCodec::GetCodecInfo获取编码信息


AudioCodec::GetCodecInfo( CodecInfo* pInfo)  // 此处的参数不应该使用单指针
{
    memcpy(pInfo, m_codecInfo, sizeof(CodecInfo));
}

上記のインターフェースのパラメーターはAudioCodec::GetCodecInfo、シングルポインターではなく、ダブルポインターである必要があります。変更されたコードは、次のようになります。

AudioCodec::GetCodecInfo( CodecInfo** pInfo)  // 此处的参数类型使用双指针
{
    memcpy(*pInfo, m_codecInfo, sizeof(CodecInfo));
}

9. exeプログラムを公開するときは、exeが依存するCランタイムライブラリとMFCライブラリを忘れてください。

たとえば、初心者はVS-MFCライブラリを使用して、テスト用のツールソフトウェアを作成します。その結果、リリースバージョンのプログラムがリリースされたときに、プログラムが依存するCランタイムライブラリが付属していないため、ツールが発生します。一部のコンピューターで起動し、エラーが見つからないことを示すエラーをCランタイムライブラリに報告するソフトウェア:

プログラムはランタイムライブラリとMFCライブラリの動的バージョンに依存しているため、プログラムを公開するときにこれらのライブラリを持参する必要があります。一部のシステムにはこれらのライブラリがなく、プログラムを起動すると、ライブラリが見つからないと報告され、起動に失敗します。

10.深いコピーを使用する必要がありますが、浅いコピーを使用してください

元々、ディープコピーが実行されるはずでしたが、シャローコピー(直接割り当て)が使用されたため、ライフサイクルが異なる別のC ++オブジェクトが同じメモリを指すようになりました。あるオブジェクトがメモリを解放した後、別のオブジェクトがこのメモリを再利用すると、メモリアクセス違反が発生し、例外が発生します。

古典的なC++で書かれたテストの質問があります。文字列クラスの関連関数を実装しましょう。その主な目的は、ディープコピーとシャローコピーの理解を調べることです。Stringクラスの宣言は、タイトルに記載されています。

class String{
public:
    String();
    String(const String & str);
    String(const char* str);
    String& operator=(String str);
    char* c_str() const;
    ~String();
    int size() const;
private:
    char* data;
};

上記の関数の内部実装を書いてみましょう。これらの関数の実装コードは次のとおりです。

//普通构造函数  
String::String(const char *str)
{
  if (str == NULL)
  {
    m_data = new char[1];// 得分点:对空字符串自动申请存放结束标志'\0'的,加分点:对m_data加NULL判断  
    *m_data = '\0';
  }
  else
  {
    int length = strlen(str);
    m_data = new char[length + 1];// 若能加 NULL 判断则更好
    strcpy(m_data, str);
  }
}
 
 
// String的析构函数  
String::~String(void)
{
  delete[] m_data; // 或delete m_data;  
}
 
 
//拷贝构造函数  
String::String(const String &other)// 得分点:输入参数为const型  
{     
  int length = strlen(other.m_data);
  m_data = new char[length + 1];// 若能加 NULL 判断则更好  
  strcpy(m_data, other.m_data);
}
 
 
//赋值函数  
String & String::operator = (const String &other) // 得分点:输入参数为const型  
{
  if (this == &other)//得分点:检查自赋值  
    return *this; 
  if (m_data)
      delete[] m_data;//得分点:释放原有的内存资源  
  int length = strlen(other.m_data);
  m_data = new char[length + 1];//加分点:对m_data加NULL判断  
  strcpy(m_data, other.m_data);
  return *this;//得分点:返回本对象的引用    
}

簡単な共有と幸せな学習、間違いがあれば許してください!

PS:共有、いいね、視聴へようこそ。

おすすめ

転載: blog.csdn.net/weixin_41055260/article/details/123887786