Introduction
I work a lot with students and I know their programming errors well. Moreover
I know my errors well, too! There is one technique that makes your program
reliable. It is a trap technique that uses the assertion macros. Common C++
techniques and some VC++/MFC features are also described.
Why must a pointer be initialized to NULL?
If you have
class A{
public:
A();
~A();
private:
pSomeType* m_pPointer;
};
A::~A(){
delete m_pPointer;
}
And if you assign a pointer outside of a constructor you always must set it
to NULL
(for example):
A::A():m_pPointer(NULL){}
Why?
- If the logic of the execution of your program does not create an object for
this pointer the delete operator will be executed without errors (according the
c++ language standard). If you forget the initialization of the bypass pointer
it will have a random value and the deletion of this address can crash your
program.
- Using break points and trace you can easily find non-initialized pointers.
- You can use "if" operator to do for a valid pointer something as
if(m_pPointer){
m_pPointer->DoSomething();
}
else{
AfxMessageBox("Unexpected error #1234."
" Send me letter please to aaa@bbb.com");
}
Probably your program will not work correctly but it will not crash in this
place. It is very important. Imagine you were entering text for two hours and
have not saved it. What do you prefer - crashing of the text editor or some
warning?
- You can set debugging traps using
ASSERT
macros.
Traps for debugging
Insertion of break points is a good technique but they are not effective if a
problem occurs inside some long circle. For example, some condition becomes
wrong after 10000 circles of execution. For catching such problem VC++/MFC
programmers use ASSERT
macros. The ANSI assert macro is often used
too. I use the MFC ASSERT
in examples below, but it does not
matter.
How is it used - (Some Examples)
ASSERT(nWaterTemperature > 0
&& nWaterTemperature < 100);
ASSERT(pSomePointer);
ASSERT(0);
How does it work?
The assertion is executed as a dialog window with some assertion info (the
program, the module, the assertion line) if the condition is false
.
The dialog has three buttons: "Break", "Repeat"("Debug"), "Continue"("Ignore").
"Break" ends the program, "Continue" ignores assertion. The most useful is the
"Repeat" button. Press it to open source editor in the assertion place. You can
test all variables' values here and understand what happened.
How is it used?
For the control of the incoming pointers:
void SomeFun(SomeType* pPointer)
{
ASSERT(pPointer);
}
For the trapping of strange values in "switch" and "if" operators.
switch(nRGBColors){
case nRed: {
case nGreen: {
case nBlue: {
default: ASSERT(0);
}
if(nWaterTemp >=0 && nWaterTemp < 50){
}
else if(nWaterTemp >= 50 && nWaterTemp <= 100){
}
else{
ASSERT(0);
}
For the values assertion
ASSERT(nSomeValue >= MinValue and nSomeValue <= MaxValue);
ASSERT(nOtherValue != 0);
Always use this technique and you will be greatly surprised how often such
traps will work!
Lovely ASSERT error
ASSERT( m_MyWnd.Create() );
Oh! It is an awful error! The program will work while debugging and will not
work in the release mode. Remember: this is a macro that is removed in the
release mode. Your window will never be created in this way. If you use MFC do
so:
VERIFY( m_MyWnd.Create() );
It works as ASSERT
in the debug mode and executes
m_MyWnd.Create()
in the release mode.
Object verification and the ASSERT_VALID MFC macro
Using verify member of class is a wide-known technique for verification
objects. If you have clear validate conditions for your object you can create
and use the verify class member.
class Time
{
public:
void Set(int h, int m);
bool Verify(){return m_h>=0 && m_h<24 and m_m>=0 && m_m<60; }
};
void Time::Set(int h, int m)
{
m_h = h;
pm_m = m;
ASSERT(Verify());
}
Most MFC classes are children of CObject
. It has the
AssertValid
virtual function that uses for verification. If a class
has the implementation of this function it is called by
ASSERT_VALID
macros. For example:
ASSERT_VALID(pView);
It checks the pointer of some CViewWnd
object. If the object is
invalid (null pointer or wrong window's handle) assertion is executed.
MFC TRACE macros
The excursus to MFC macros is incompatible without the description of TRACE
macros . There is no problem in outputting the values of variables using streams
in the console mode. From the other side tracing variables under Windows
programming is not such a trivial task. Actually a lot of windows can be opened
and closed while we are tracing something. There is no sense in making a trace
output to many windows. One window is enough. For this purpose VC++ IDE uses
"output" window (View-Output menu point). For debugging output you can use TRACE
operator that has the same format as the printf
STDIO
function.
Here are some examples :-
TRACE("\nThis is a trace of int variable %d.", nSomeInt);
TRACE("\nFunction OnInitialUpdate is starting.");
Links
(C) Alex Rest, 2003