📄 suggest.htm
字号:
you write alternative constructors, then you can't use them with <TT>CreateForm</TT>.
</p>
<p>
<B>5-</B> What happens if you call <TT>CreateForm</TT> and your form does not have a constructor that
takes a single <TT>TComponent *</TT> as an owner? The answer is 'bad stuff happens'. Try it
out. Create a new application. In the main form, add an <TT>AnsiString</TT> parameter to the
existing constructor. Then add a <TT>ShowMessage</TT> or something to the body of the constructor. Like this:
</P>
<pre>
<b>__fastcall</b> TForm1<b>:</b><b>:</b>TForm1<b>(</b>TComponent<b>*</b> Owner<b>,</b> <b>const</b> AnsiString <b>&</b>foo<b>)</b>
<b>:</b> TForm<b>(</b>Owner<b>)</b>
<b>{</b>
ShowMessage<b>(</b><font color="blue">"Foo = "</font> <b>+</b> foo<b>)</b><b>;</b>
<b>}</b>
</pre>
<P>
Make sure you change the header file too. Compile and link the app (no warnings or
errors occur). Put a breakpoint on the <TT>ShowMessage</TT> call (if you can, hint hint). Run the
app. Notice anything missing? Does the <TT>ShowMessage</TT> line ever execute?
</p>
<p>
No it doesn't. How come? How can a class be constructed without calling its constructor? The
answer lies in that virtual constructor stuff. When you change the argument pattern
for the constructor, you are no longer overriding the virtual constructor of the base
class. Since <TT>CreateForm</TT> invokes the virtual constructor, and because your class does
not override that virtual function, code execution jumps straight to the constructor
of the base class, completely bypassing the derived class (ie *your* class). The
initialization of your form and all its member variables is circumvented.
</P>
<P>
For these reasons, it is wise to use operator <TT>new</TT> for code that you write. <TT>CreateForm</TT> is one of those
pascal things that C++ programmers are wise to avoid.
</P>
<BR>
<H3>
<A NAME="nullptr">Always set a pointer variable to NULL or 0 after deleting it</A>
</H3>
<P>
Dereferencing a <TT>NULL</TT> pointer will always cause an access violation. Dereferencing a non-null pointer that has
been deleted will sometimes raise an access violation, but many times, your program will continue to run. Sometimes,
the program will appear to behave normally, and then mysteriously crash on you without warning. For debugging purposes,
it's best to try and force your program to always crash when you dereference a pointer that has already been deleted.
It is much easier to fix a program that always fails than it is to fix a program that sometimes fails.
</P>
<P>
<B>Note:</B> A couple of readers have pointed out some problems with this suggestion. Hendrik Schober had this to say
</P>
<I>
<P>
[Always setting] a pointer variable to NULL or 0 after deleting it
has one disadvantage: It makes it harder to find multiple deletes.
</P>
<P>
Hendrik Schober
</P>
</I>
<P>
He has a good point. Deleting a null pointer is guaranteed to be safe. However, deleting the same pointer multiple times may be the
result of poor coding. Setting your pointers to <TT>NULL</TT> will mask this problem. Chris Uzdavinis, a fellow TeamB member for C++
also had some comments:
</P>
<I>
<P>
There are two things that I think should be mentioned.
</P>
<P>
1) class member pointers that point to objects deallocated in a destructor
need not be zeroed, because the class containing the pointer is going away
and the dangling pointer problem doesn't exist. Zeroing the pointer is pure
overhead without any benefit.
</P>
<P>
2) If a pointer has its object deleted, something non-zero but recognizable
is preferable, IMHO, because when a particular address is referenced that is
known to be out-of bounds, then you know what the error is immediately.
Null pointers may or may not be problems.
</P>
<P>
Also, nullifying pointers encourages bad coding. People stop paying as much
attention to writing correct code. Instead, there are lots of "if (x)
x->do_someting()" kinds of statements, because the programmers seem to stop
caring about if the pointer should be valid.
</P>
<P>
...
</P>
<P>
If a pointer must be set to anything, I think a non-zero value that is
guarenteed to be invalid is a better solution.
</P>
<P>
Chris Uzdavinis
</P>
</I>
<P>
Both items are true. Point #1 highlights the fact that assigning NULL to all of
your pointers is going to cost you some CPU cycles. For the most part, those CPU cycles
are wasted cycles. Point #2 suggests that you use some other, non-zero constant to
make the assignment. What's the benefit of this? Well, if you try to access a null
pointer, the error box will say "Read of address 0". This error message does not provide
much information about where the access violation occurred. Using a non-zero constant can help
you isolate the problem.
</P>
<P>
You should way the pros and cons of this suggestion before deciding whether or not
you actually want to use this technique.
</P>
<BR>
<H3>
<A NAME="makefile">Don't alter the makefile switches for alignment or enum variables</A>
</H3>
<P>
<B>Note:</B> This suggestion applies to BCB3. Borland fixed the VCL headers in BCB4 and BCB5. You can now change the project
options for alignment and enums in BCB4 and BCB5 without worrying about messing up the VCL. The VCL headers contain <TT>#pragma</TT>
guards to ensure correct alignment for VCL objects.
</P>
<P>
The VCL relies on dword alignment (-a4) and enum types that are not forced to be the same size as an int (-b-).
If you switch the alignment mode to byte size alignment (-a1) or word alignment (-a2), you will start to see some strange access
violations when you run your program. The same applies if you turn on the treat enums as integers compiler option (-b).
</P>
<P>
If you have a section of code that requires a different setting for alignment or enums, you can use the <TT>#pragma push</TT> and
<TT>#pragma pop</TT> compiler directives.
</P>
<pre>
<font color="green">#pragma option push -a1</font>
<b>struct</b> Foo
<b>{</b>
<b>int</b> x<b>;</b>
<b>int</b> y<b>;</b>
<b>}</b><b>;</b>
<font color="green">#pragma pop</font>
</pre>
<BR>
<H3>
<A NAME="vclib">Don't link with LIB files or OBJ files created by other compilers</A>
</H3>
<P>
Visual C++ uses a derivative of the COFF file format format for LIB and OBJ files. Borland compilers use the OMF
format. The two are not compatible. You cannot link with a LIB or OBJ file that was created with Visual C++ (or any
other compiler for that matter).
</P>
<BR>
<H3>
<A NAME="bclib">Don't link with OBJ files created by a previous version of the Borland compiler</A>
</H3>
<P>
The OBJ file format changed in C++Builder 3. OBJ files created with older versions of C++Builder or Borland C++ are
not compatible with the new format.
</P>
<BR>
<H3>
<A NAME="tlibimp">Don't import COM type libraries using the IDE menu option</A>
</H3>
<P>
<B>Note:</B> This suggestion applies only to BCB3. Ignore this suggestion for BCB4, BCB5, and newer.
</P>
<P>
C++Builder 3 contained a bug that made it difficult, if not impossible, to import type libraries. The patch to
C++Builder 3 fixes many of these problems. However, the patch only fixes the command line TLIBIMP.EXE utility. The
patch does not fix the code in the IDE that imports a type library when you choose the Project | Import Type Library
menu item.
<P>
If you experience problems importing a particular type library, try to import the library using
the command line utility instead of using the IDE. After running TLIBIMP, add the resulting files to your project.
</P>
<BR>
<H3>
<A NAME="oldcreateorder">When you import BCB3 forms into BCB4, set OldCreateOrder to false</A>
</H3>
<P>
Be careful when you migrate your projects from BCB3 to BCB4. You need to watch out for the
<TT>OldCreateOrder</TT> property on your forms and datamodules. For some reason, when you import
your BCB3 forms into BCB4, the <TT>OldCreateOrder</TT> property gets set to true. Having <TT>OldCreateOrder</TT>
set to true can cause access violations when your forms are created or destroyed.
</P>
<P>
In C++Builder 4, <TT>TForm</TT> contains a new property called <TT>OldCreateOrder</TT>. If this property is true, the
<TT>OnCreate</TT> event of your forms will execute before the constructor runs. Actually, the <TT>OnCreate</TT> handler
will fire from within the <TT>Create</TT> method of <TT>TCustomForm</TT>. Recall that in C++, base class
constructors execute before constructors in derived classes.
</P>
<P>
Having the <TT>OnCreate</TT> handler fire before your constructor runs creates a scenario that is compatible with
Delphi 1, 2, and 3, and C++Builder 1. However, this backward compatability can also cause memory problems. If
<TT>OldCreateOrder</TT> is true, your <TT>OnCreate</TT> handler will run before your constructor has a chance to
initialize variables and construct member objects. Access violations can occur if your <TT>OnCreate</TT> handler
attempts to dereference or use member variables. For this reason, it is best to always leave <TT>OldCreateOrder</TT>
false. When <TT>OldCreateOrder</TT> is false, the <TT>OnCreate</TT> event is fired after your constructor runs.
This mode is compatible with C++Builder 3.
</P>
<P>
If you upgrade a project to C++Builder 4, you should open each form in your project and manually set
<TT>OldCreateOrder</TT> to false. You can leave <TT>OldCreateOrder</TT> set on true if you don't write
<TT>OnCreate</TT> or <TT>OnDestroy</TT> handlers, but this could cause code maintenance headaches down the road.
</P>
<P>
The <TT>OldCreateOrder</TT> property also applies to the <TT>OnDestroy</TT> event. The following table explains how
<TT>OldCreateOrder</TT> affects the sequence of events during a form's lifetime.
</P>
<PRE>
<B>Table 1</B>: Understanding the OldCreateOrder property in C++Builder 4.0
===========================================================================
OldCreateOrder = false OldCreateOrder = true
---------------------------------------------------------------------------
TCustomForm.Create TCustomForm.Create
TMyForm::TMyForm() TMyForm::OnCreate
TMyForm::OnCreate TMyForm::TMyForm()
... ...
TMyForm::OnDestroy TMyForm::~TMyForm()T
TMyForm::~TMyForm() TMyForm::OnDestroy
TCustomForm.Destroy TCustomForm.Destroy
--------------------------------------------------------------------------
</PRE>
<BR>
<H3>
<A NAME="asfunction">Don't use AsDateTime or AsInteger to assign one TField to another</A>
</H3>
<P>
Assigning the value of one field to another is a common task in database applications. In C++Builder or Delphi
programs, you may find yourself writing code like this:
<pre>
Table1<b>-></b>FieldByName<b>(</b>seq_no<b>)</b><b>-></b>AsInteger <b>=</b> Table2seq_no<b>-></b>AsInteger<b>;</b>
Table1<b>-></b>FieldByName<b>(</b>date<b>)</b> <b>-></b>AsDateTime <b>=</b> Table2date <b>-></b>AsDateTime<b>;</b>
</pre>
<P>
As long as the fields in <TT>Table2</TT> are not null, these two code statements work great. What happens when
the fields in <TT>Table2</TT> are null (ie, the fields contain no value)? You might think that the fields in
<TT>Table1</TT> will also be null after the <TT>AsInteger</TT> and <TT>AsDateTime</TT> assignments.
</P>
<P>
This isn't the way it works. When you assign a null field to another
field through the <TT>AsInteger</TT> property, the VCL copies over a value of 0. The field will not be null.
Likewise, the datetime field wont be null if you use <TT>AsDateTime</TT> to assign its value. It will also
contain the value 0, which corresponds to a date of Jan 1 1900. In the sample above, the integer field in
<TT>Table1</TT> will contain the value 0 if <TT>Table2seq_no</TT> is empty, and the date field of <TT>Table1</TT> will
contain the date Jan 1 1900 if <TT>Table2date</TT> is null.
</P>
<P>
Assigning a null string field to another field through the <TT>AsString</TT> property works ok. However,
<TT>AsCurrency</TT>, <TT>AsBoolean</TT>, and <TT>AsFloat</TT> suffer from the same problems that plague
<TT>AsDateTime</TT> and <TT>AsInteger</TT>. As a workaround, you can use the <TT>AsVariant</TT> property.
<TT>Variant</TT> types support the concept of a null value. If the source field is null, the destination field
will also be null if you use the <TT>AsVariant</TT> property to assign its value. As an added bonus, you can use
<TT>AsVariant</TT> on almost all field types. To use the <TT>AsVariant</TT> property, change the previous code to:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -