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
Penegasan dalam build Debug dan Rilis
Efek samping penggunaan penegasan
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.
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.
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));
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.
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.
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.
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.