Bagikan melalui


Pernyataan C/C++

Pernyataan penegasan menentukan kondisi yang Anda harapkan benar pada suatu titik dalam program Anda. Jika kondisi tersebut tidak benar, penegasan akan gagal, eksekusi program Anda terganggu, dan kotak dialog Penegasan Gagal akan muncul.

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

  • Penegasan MFC untuk program MFC.

  • ATLASSERT untuk program yang menggunakan ATL.

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

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

    Anda dapat menggunakan penegasan untuk menemukan kondisi kesalahan logika, memeriksa hasil operasi, dan menguji kondisi kesalahan yang seharusnya ditangani.

Dalam topik ini

Cara kerja penegasan

Penegasan dalam build Debug dan Rilis

Efek samping penggunaan penegasan

Penegasan CRT

Penegasan MFC

Cara kerja penegasan

Ketika debugger berhenti karena MFC atau penegasan pustaka run-time C, debugger menavigasi ke titik dalam file sumber tempat penegasan terjadi jika sumbernya tersedia. Pesan penegasan muncul di jendela Output dan kotak dialog Penegasan Gagal. Anda dapat menyalin pesan penegasan 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 teliti karena memberikan petunjuk tentang penyebab kegagalan penegasan.

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

Dalam topik ini

Penegasan dalam build Debug dan Rilis

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

Efek samping penggunaan penegasan

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

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

Karena ekspresi ASSERT 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, bukan ASSERT. VERIFY mengevaluasi ekspresi di semua versi tetapi tidak memeriksa hasilnya dalam versi Rilis.

Berhati-hatilah dalam menggunakan panggilan fungsi dalam pernyataan penegasan, karena mengevaluasi fungsi dapat memiliki efek samping 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 membebankan overhead dari panggilan fungsi yang tidak penting dalam versi Rilis.

Dalam topik ini

Penegasan CRT

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

Makro Hasil
_ASSERT Jika ekspresi yang ditentukan bernilai FALSE, nama file dan nomor baris dari _ASSERT.
_ASSERTE Sama seperti _ASSERT, ditambah representasi string dari ekspresi yang ditegaskan.

_ASSERTE lebih kuat karena melaporkan ekspresi tegas 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 ditegaskan menggunakan _ASSERTE. Jika Anda menggunakan banyak makro _ASSERTE, ekspresi string ini membutuhkan memori dalam jumlah besar. Jika terbukti bermasalah, gunakan _ASSERT untuk menghemat memori.

Ketika _DEBUG didefinisikan, makro _ASSERTE didefinisikan sebagai berikut:

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

Jika ekspresi yang ditegaskan bernilai FALSE, _CrtDbgReport dipanggil untuk melaporkan kegagalan penegasan (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 penegasan sementara waktu, gunakan _CtrSetReportMode.

Memeriksa Adanya Kerusakan Timbunan

Contoh berikut menggunakan _CrtCheckMemory untuk memeriksa kerusakan timbunan:

_ASSERTE(_CrtCheckMemory());

Memeriksa Validitas Penunjuk

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 penunjuk menunjuk ke memori di timbunan lokal (timbunan yang dibuat dan dikelola oleh instans pustaka run-time C — DLL dapat memiliki instans pustaka sendiri, dan oleh karena itu memiliki timbunannya sendiri, di luar timbunan aplikasi). Penegasan ini tidak hanya menangkap null atau alamat di luar batas, tetapi juga penunjuk ke variabel statis, variabel tumpukan, dan memori non-lokal 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

Penegasan MFC

MFC mendefinisikan makro ASSERT untuk pemeriksaan penegasan. Juga mendefinisikan metode MFC ASSERT_VALID dan CObject::AssertValid untuk memeriksa status objek turunan CObject.

Jika argumen makro ASSERT MFC bernilai nol atau salah, makro menghentikan eksekusi program dan memperingatkan pengguna; jika tidak, eksekusi dilanjutkan.

Saat penegasan gagal, kotak dialog pesan memperlihatkan nama file sumber dan nomor baris penegasan. Jika Anda memilih Coba lagi dalam kotak dialog, panggilan pada AfxDebugBreak menyebabkan eksekusi terputus ke debugger. Pada saat itu, Anda dapat memeriksa tumpukan panggilan dan menggunakan fasilitas debugger lainnya untuk menentukan mengapa penegasan gagal. Jika Anda telah mengaktifkan Penelusuran kesalahan just-in-time, dan debugger belum berjalan, kotak dialog 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 ) ) );

Makro ASSERT tidak menghasilkan kode dalam versi Rilis. Jika Anda perlu mengevaluasi ekspresi dalam versi Rilis, gunakan makro VERIFY, bukan ASSERT.

MFC ASSERT_VALID dan CObject::AssertValid

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

Contoh berikut menunjukkan cara mendeklarasikan fungsi AssertValid:

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

Saat Anda mengambil alih AssertValid, panggil versi AssertValid kelas dasar sebelum Anda melakukan pemeriksaan Anda sendiri. Kemudian, gunakan makro ASSERT untuk memeriksa anggota yang unik ke 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 makro ASSERT_VALID untuk menguji validitas internalnya (jika kelasnya mengambil alih AssertValid).

Misalnya, pertimbangkan kelas CMyData, yang menyimpan CObList di salah satu variabel anggotanya. Variabel CObList, m_DataList, menyimpan kumpulan objek CPerson. Deklarasi singkatan CMyData terlihat 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 ...
};

Pengambilalihan 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 menggunakan mekanisme AssertValid untuk menguji validitas objek yang disimpan dalam anggota datanya. Pengambilalihan AssertValid dari CMyData memanggil makro ASSERT_VALID untuk variabel anggota m_pDataList miliknya sendiri.

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

Dengan beberapa pekerjaan lagi, Anda dapat menambahkan pengujian validitas untuk objek CPerson yang juga disimpan dalam daftar tersebut. Anda dapat memperoleh kelas CPersonList dari CObList dan mengambil alih AssertValid. Dalam pengambilalihan, Anda akan memanggil CObject::AssertValid lalu melakukan perulangan melalui daftar, dengan memanggil AssertValid pada setiap objek CPerson yang disimpan dalam daftar tersebut. Kelas CPerson yang ditunjukkan di awal topik ini sudah mengambil alih AssertValid.

Ini adalah mekanisme yang kuat ketika Anda membangun untuk penelusuran kesalahan. Ketika Anda membangun untuk rilis, mekanisme ini dinonaktifkan secara otomatis.

Batasan AssertValid

Penegasan yang dipicu menunjukkan bahwa objek tersebut jelas buruk dan eksekusi akan berhenti. Namun, kurangnya penegasan hanya menunjukkan bahwa tidak ada masalah yang ditemukan, tetapi objek tidak dijamin bagus.

Dalam topik ini

Menggunakan penegasan

Menemukan kesalahan logika

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

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

ASSERT(numMols >= 0);

Atau Anda mungkin menyertakan penegasan CRT seperti ini:

_ASSERT(numMols >= 0);

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

Dalam topik ini

Memeriksa hasil

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

Misalnya, pertimbangkan kode berikut, yang memperbarui variabel iMols berdasarkan konten daftar tertaut yang ditunjukkan 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, yaitu numMols. Inspeksi visual perulangan tidak menunjukkan bahwa ini akan selalu terjadi, sehingga pernyataan penegasan digunakan setelah perulangan untuk menguji kondisi tersebut.

Dalam topik ini

Menemukan kesalahan yang tidak tertangani

Anda dapat menggunakan penegasan 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 direset ke nol sebelum penegasan tercapai. Jika myErr memiliki nilai lain, penegasan akan gagal, program berhenti, dan Kotak Dialog Penegasan Gagal akan muncul.

Namun, pernyataan penegasan bukan pengganti kode penanganan kesalahan. Contoh berikut menunjukkan penegasan 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 penegasan untuk menangani kondisi kesalahan. Akibatnya, kode kesalahan apa pun yang dikembalikan oleh myGraphRoutine akan tidak tertangani dalam kode rilis akhir.

Dalam topik ini