⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 suggest.htm

📁 C++builder学习资料C++builder
💻 HTM
📖 第 1 页 / 共 4 页
字号:
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>&amp;</b>foo<b>)</b>

    <b>:</b> TForm<b>(</b>Owner<b>)</b>

<b>{</b>

    ShowMessage<b>(</b><font color="blue">&quot;Foo = &quot;</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>-&gt;</b>FieldByName<b>(</b>seq_no<b>)</b><b>-&gt;</b>AsInteger  <b>=</b> Table2seq_no<b>-&gt;</b>AsInteger<b>;</b>

    Table1<b>-&gt;</b>FieldByName<b>(</b>date<b>)</b>  <b>-&gt;</b>AsDateTime <b>=</b> Table2date  <b>-&gt;</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 + -