Bagikan melalui


Pernyataan C/C++

Pernyataan asersi menentukan kondisi yang Anda harapkan benar pada titik tertentu dalam program Anda. Jika kondisi tersebut tidak benar, pernyataan gagal, eksekusi program Anda terganggu, dan kotak dialog Pernyataan Gagal muncul.

Visual Studio mendukung pernyataan pernyataan C++ yang didasarkan pada konstruksi berikut:

  • Pernyataan MFC untuk program MFC.

  • ATLASSERT untuk program yang menggunakan ATL.

  • Pernyataan CRT untuk program yang menggunakan pustaka run-time C.

  • Fungsi pernyataan ANSI untuk program C/C++ lainnya.

    Anda dapat menggunakan pernyataan untuk menangkap kesalahan logika, memeriksa hasil operasi, dan Menguji kondisi kesalahan yang seharusnya ditangani.

Dalam topik ini

Cara kerja pernyataan

Pernyataan dalam build Debug dan Rilis

Efek samping menggunakan pernyataan

Pernyataan CRT

Pernyataan MFC

Cara kerja pernyataan

Ketika debugger berhenti karena pernyataan pustaka run-time MFC atau C, maka jika sumber tersedia, debugger menavigasi ke titik dalam file sumber tempat pernyataan terjadi. Pesan pernyataan muncul di jendela Output dan kotak dialog Pernyataan Gagal . Anda dapat menyalin pesan pernyataan dari jendela Output ke jendela teks jika Anda ingin menyimpannya untuk referensi di masa mendatang. Jendela Output mungkin juga berisi pesan kesalahan lainnya. Periksa pesan-pesan ini dengan hati-hati, karena memberikan petunjuk tentang penyebab kegagalan pernyataan.

Gunakan pernyataan untuk mendeteksi kesalahan selama pengembangan. Sebagai aturan, gunakan satu pernyataan untuk setiap asumsi. Misalnya, jika Anda berasumsi bahwa argumen bukan NULL, gunakan pernyataan untuk menguji asumsi tersebut.

Dalam topik ini

Pernyataan dalam build Debug dan Rilis

Pernyataan hanya dikompilasi jika _DEBUG telah ditentukan. Jika tidak, kompilator memperlakukan pernyataan sebagai pernyataan null. Oleh karena itu, pernyataan penegasan tidak memberlakukan biaya overhead atau biaya performa dalam program Rilis akhir Anda, dan memungkinkan Anda untuk menghindari penggunaan direktif #ifdef.

Efek samping menggunakan pernyataan

Saat Anda menambahkan pernyataan ke kode Anda, pastikan pernyataan tidak memiliki efek samping. Misalnya, pertimbangkan pernyataan berikut yang memodifikasi nilai nM.

ASSERT(nM++ > 0); // Don't do this!

ASSERT Karena ekspresi tidak dievaluasi dalam versi Rilis program Anda, nM akan memiliki nilai yang berbeda dalam versi Debug dan Rilis. Untuk menghindari masalah ini di MFC, Anda dapat menggunakan makro VERIFY alih-alih ASSERT. VERIFY mengevaluasi ekspresi di semua versi tetapi tidak memeriksa hasilnya dalam versi Rilis.

Berhati-hatilah dengan menggunakan panggilan fungsi dalam pernyataan asersi, karena mengevaluasi fungsi dapat memiliki dampak yang tidak terduga.

ASSERT ( myFnctn(0)==1 ) // unsafe if myFnctn has side effects
VERIFY ( myFnctn(0)==1 ) // safe

VERIFY memanggil myFnctn dalam versi Debug dan Rilis, sehingga dapat diterima untuk digunakan. Namun, menggunakan VERIFY memberlakukan beban tambahan dari panggilan fungsi yang tidak diperlukan dalam versi rilis.

Dalam topik ini

Pernyataan CRT

File CRTDBG.H header mendefinisikan makro _ASSERT dan _ASSERTE untuk pemeriksaan pernyataan.

Macro Result
_ASSERT Jika ekspresi yang ditentukan mengevaluasi menjadi FALSE, maka nama file dan nomor baris dari _ASSERT ditampilkan.
_ASSERTE Sama seperti _ASSERT, ditambah representasi string ekspresi yang dinyatakan.

_ASSERTE lebih kuat karena melaporkan ekspresi asersi yang ternyata FALSE. Ini mungkin cukup untuk mengidentifikasi masalah tanpa mengacu pada kode sumber. Namun, versi Debug aplikasi Anda akan berisi konstanta string untuk setiap ekspresi yang dinyatakan menggunakan _ASSERTE. Jika Anda menggunakan banyak _ASSERTE makro, ekspresi string ini mengambil sejumlah besar memori. Jika itu terbukti menjadi masalah, gunakan _ASSERT untuk menyimpan memori.

Ketika _DEBUG didefinisikan, _ASSERTE makro didefinisikan sebagai berikut:

#define _ASSERTE(expr) \
    do { \
        if (!(expr) && (1 == _CrtDbgReport( \
            _CRT_ASSERT, __FILE__, __LINE__, #expr))) \
            _CrtDbgBreak(); \
    } while (0)

Jika ekspresi yang ditegaskan mengevaluasi ke FALSE, _CrtDbgReport dipanggil untuk melaporkan kegagalan pernyataan (menggunakan kotak dialog pesan secara default). Jika Anda memilih Coba Lagi dalam kotak dialog pesan, _CrtDbgReport mengembalikan 1 dan _CrtDbgBreak memanggil debugger melalui DebugBreak.

Jika Anda perlu menonaktifkan semua pernyataan untuk sementara waktu, gunakan _CtrSetReportMode.

Memeriksa Kerusakan Timbunan

Contoh berikut menggunakan _CrtCheckMemory untuk memeriksa kerusakan tumpukan:

_ASSERTE(_CrtCheckMemory());

Memeriksa Validitas Pointer

Contoh berikut menggunakan _CrtIsValidPointer untuk memverifikasi bahwa rentang memori tertentu valid untuk membaca atau menulis.

_ASSERTE(_CrtIsValidPointer( address, size, TRUE );

Contoh berikut menggunakan _CrtIsValidHeapPointer untuk memverifikasi bahwa penunjuk menunjuk ke memori dalam heap lokal (heap yang dibuat dan dikelola oleh instance pustaka run-time C ini — DLL dapat memiliki instance pustakanya sendiri, dan oleh karena itu heap-nya sendiri, di luar heap aplikasi). Pernyataan ini tidak hanya menangkap alamat null atau out-of-bounds, tetapi juga penunjuk ke variabel statis, variabel tumpukan, dan memori nonlokal lainnya.

_ASSERTE(_CrtIsValidHeapPointer( myData );

Memeriksa Blok Memori

Contoh berikut menggunakan _CrtIsMemoryBlock untuk memverifikasi bahwa blok memori berada di timbunan lokal dan memiliki jenis blok yang valid.

_ASSERTE(_CrtIsMemoryBlock (myData, size, &requestNumber, &filename, &linenumber));

Dalam topik ini

Pernyataan MFC

MFC mendefinisikan makro ASSERT untuk pemeriksaan pernyataan. Ini juga mendefinisikan metode MFC ASSERT_VALID dan CObject::AssertValid untuk memeriksa keadaan internal objek turunan dari CObject.

Jika argumen makro MFC ASSERT mengevaluasi ke nol atau salah, makro menghentikan eksekusi program dan memperingatkan pengguna; jika tidak, eksekusi berlanjut.

Saat pernyataan gagal, kotak dialog pesan memperlihatkan nama file sumber dan nomor baris pernyataan. Jika Anda memilih Coba Lagi dalam kotak dialog, panggilan ke AfxDebugBreak menyebabkan eksekusi dialihkan ke debugger. Pada saat itu, Anda dapat memeriksa tumpukan panggilan dan menggunakan fasilitas debugger lainnya untuk menentukan mengapa pernyataan gagal. Jika Anda telah mengaktifkan penelusuran kesalahan Just-in-time, dan debugger belum berjalan, kotak dialog akan dapat meluncurkan debugger.

Contoh berikut menunjukkan cara menggunakan ASSERT untuk memeriksa nilai pengembalian fungsi:

int x = SomeFunc(y);
ASSERT(x >= 0);   //  Assertion fails if x is negative

Anda dapat menggunakan ASSERT dengan fungsi IsKindOf untuk memberikan pemeriksaan jenis argumen fungsi:

ASSERT( pObject1->IsKindOf( RUNTIME_CLASS( CPerson ) ) );

ASSERT Makro tidak menghasilkan kode dalam versi Rilis. Jika Anda perlu mengevaluasi ekspresi dalam versi Rilis, gunakan makro VERIFIKASI alih-alih ASSERT.

ASSERT_VALID MFC dan CObject::AssertValid

Metode CObject::AssertValid menyediakan pemeriksaan run-time dari status internal objek. Meskipun Anda tidak diharuskan untuk meng-override AssertValid ketika Anda menggunakan turunan dari kelas CObject, Anda dapat membuat kelas Anda lebih dapat diandalkan dengan melakukan ini. AssertValid harus melakukan pernyataan pada semua variabel anggota objek untuk memverifikasi bahwa variabel tersebut berisi nilai yang valid. Misalnya, harus memeriksa bahwa variabel anggota penunjuk bukan NULL.

Contoh berikut menunjukkan cara mendeklarasikan AssertValid fungsi:

class CPerson : public CObject
{
protected:
    CString m_strName;
    float   m_salary;
public:
#ifdef _DEBUG
    // Override
    virtual void AssertValid() const;
#endif
    // ...
};

Saat Anda mengambil AssertValidalih , panggil versi AssertValid kelas dasar sebelum Anda melakukan pemeriksaan Anda sendiri. Kemudian gunakan makro ASSERT untuk memeriksa anggota yang unik untuk kelas turunan Anda, seperti yang ditunjukkan di sini:

#ifdef _DEBUG
void CPerson::AssertValid() const
{
    // Call inherited AssertValid first.
    CObject::AssertValid();

    // Check CPerson members...
    // Must have a name.
    ASSERT( !m_strName.IsEmpty());
    // Must have an income.
    ASSERT( m_salary > 0 );
}
#endif

Jika salah satu variabel anggota Anda menyimpan objek, Anda dapat menggunakan ASSERT_VALID makro untuk menguji validitas internal mereka (jika kelas mereka mengambil alih AssertValid).

Misalnya, pertimbangkan kelas CMyData, yang menyimpan CObList di salah satu variabel anggotanya. Variabel CObList , m_DataListmenyimpan kumpulan CPerson objek. Deklarasi yang disingkat CMyData tampak seperti ini:

class CMyData : public CObject
{
    // Constructor and other members ...
    protected:
        CObList* m_pDataList;
    // Other declarations ...
    public:
#ifdef _DEBUG
        // Override:
        virtual void AssertValid( ) const;
#endif
    // And so on ...
};

Penggantian AssertValid dalam CMyData terlihat seperti ini:

#ifdef _DEBUG
void CMyData::AssertValid( ) const
{
    // Call inherited AssertValid.
    CObject::AssertValid( );
    // Check validity of CMyData members.
    ASSERT_VALID( m_pDataList );
    // ...
}
#endif

CMyData AssertValid menggunakan mekanisme untuk menguji validitas objek yang disimpan dalam anggota datanya. Penggantian AssertValid dari CMyData memanggil makro ASSERT_VALID untuk variabel anggota m_pDataList miliknya.

Pengujian validitas tidak berhenti pada tingkat ini karena kelas CObList juga mengambil alih AssertValid. Penggantian ini melakukan pengujian validitas tambahan pada keadaan internal daftar. Dengan demikian, pengujian validitas pada CMyData objek mengarah ke pengujian validitas tambahan untuk status internal objek daftar yang disimpan CObList .

Dengan sedikit usaha lagi, Anda dapat menambahkan pengujian validitas untuk objek yang disimpan CPerson dalam daftar. Anda bisa mendapatkan kelas CPersonList dari CObList dan mengambil alih AssertValid. Dalam penimpaan, Anda akan memanggil CObject::AssertValid dan kemudian melakukan iterasi melalui daftar, memanggil AssertValid pada setiap objek CPerson yang disimpan dalam daftar. Kelas CPerson yang ditunjukkan di awal topik ini sudah mengambil alih AssertValid.

Ini adalah mekanisme yang kuat ketika Anda melakukan kompilasi untuk penelusuran kesalahan. Saat Anda kemudian membangun untuk rilis, mekanisme dinonaktifkan secara otomatis.

Keterbatasan AssertValid

Pernyataan yang dipicu menunjukkan bahwa objek pasti buruk dan eksekusi akan berhenti. Namun, kurangnya pernyataan hanya menunjukkan bahwa tidak ada masalah yang ditemukan, tetapi objek tidak dijamin baik.

Dalam topik ini

Menggunakan asersi

Menangkap kesalahan logika

Anda dapat menetapkan pernyataan pada kondisi yang harus benar sesuai dengan logika program Anda. Pernyataan tidak berpengaruh kecuali terjadi kesalahan logika.

Misalnya, Anda mensimulasikan molekul gas dalam kontainer, dan variabel numMols mewakili jumlah total molekul. Angka ini tidak boleh kurang dari nol, jadi Anda mungkin menyertakan pernyataan pernyataan MFC seperti ini:

ASSERT(numMols >= 0);

Atau Anda mungkin menyertakan pernyataan CRT seperti ini:

_ASSERT(numMols >= 0);

Pernyataan ini tidak melakukan apa pun jika program Anda beroperasi dengan benar. Namun, jika kesalahan logika menyebabkan numMols kurang dari nol, pernyataan menghentikan eksekusi program Anda dan menampilkan Kotak Dialog Gagal Pernyataan.

Dalam topik ini

Memeriksa hasil

Pernyataan berharga untuk operasi pengujian yang hasilnya tidak jelas dari inspeksi visual cepat.

Misalnya, pertimbangkan kode berikut, yang memperbarui variabel iMols berdasarkan konten daftar tertaut yang ditujukkan oleh mols:

/* This code assumes that type has overloaded the != operator
 with const char *
It also assumes that H2O is somewhere in that linked list.
Otherwise we'll get an access violation... */
while (mols->type != "H2O")
{
    iMols += mols->num;
    mols = mols->next;
}
ASSERT(iMols<=numMols); // MFC version
_ASSERT(iMols<=numMols); // CRT version

Jumlah molekul yang dihitung oleh iMols harus selalu kurang dari atau sama dengan jumlah total molekul, numMols. Inspeksi visual terhadap perulangan tidak menunjukkan bahwa ini harus demikian, sehingga pernyataan asersi digunakan setelah perulangan untuk menguji kondisi tersebut.

Dalam topik ini

Menemukan kesalahan yang tidak tertangani

Anda dapat menggunakan pernyataan untuk menguji kondisi kesalahan pada titik dalam kode Anda di mana kesalahan apa pun seharusnya ditangani. Dalam contoh berikut, rutinitas grafik mengembalikan kode kesalahan atau nol untuk keberhasilan.

myErr = myGraphRoutine(a, b);

/* Code to handle errors and
   reset myErr if successful */

ASSERT(!myErr); -- MFC version
_ASSERT(!myErr); -- CRT version

Jika kode penanganan kesalahan berfungsi dengan baik, kesalahan harus ditangani dan myErr diatur ulang ke nol sebelum pernyataan tercapai. Jika myErr memiliki nilai lain, pernyataan gagal, program berhenti, dan Kotak Dialog Gagal Pernyataan muncul.

Pernyataan asersi bukanlah pengganti kode penanganan kesalahan, namun. Contoh berikut menunjukkan pernyataan pernyataan yang dapat menyebabkan masalah dalam kode rilis akhir:

myErr = myGraphRoutine(a, b);

/* No Code to handle errors */

ASSERT(!myErr); // Don't do this!
_ASSERT(!myErr); // Don't do this, either!

Kode ini bergantung pada pernyataan pernyataan untuk menangani kondisi kesalahan. Akibatnya, kode kesalahan apa pun yang dikembalikan oleh myGraphRoutine akan tidak tertangani dalam kode rilis akhir.

Dalam topik ini