📄 ch03.htm
字号:
in
Object Pascal, which they mimic. Object Pascal or BASIC programmers might find
that the next line of code looks a little like a simple type declaration. It is not.
Instead, this code calls the constructor for an object:</P>
<PRE><FONT
COLOR="#0066FF">AnsiString S;
</FONT></PRE>
<P>Here are the available constructors for the <TT>AnsiString</TT> class:</P>
<PRE><FONT COLOR="#0066FF">__fastcall AnsiString(): Data(0) {}
__fastcall AnsiString(const char* src);
__fastcall
AnsiString(const AnsiString& src);
__fastcall AnsiString(const char* src, unsigned char len);
__fastcall AnsiString(const wchar_t* src);
__fastcall AnsiString(char src);
__fastcall AnsiString(int src);
__fastcall AnsiString(double src);
</FONT></PRE>
<P>The simple <TT>AnsiString</TT> declaration shown at the beginning of this section
would call the first constructor shown previously, which initializes to zero a private
variable of the <TT>AnsiString</TT> class. This private variable,
named <TT>Data</TT>,
is of type <TT>char *</TT>. <TT>Data</TT> is the core C string around which the <TT>AnsiString</TT>
class is built. In other words, the <TT>AnsiString</TT> class is a wrapper around
a simple C string, and the class exists to make
it easy to manipulate this string
and to make the string compatible with the needs of the VCL.</P>
<P>The following simple declaration would call the second constructor shown in the
previous list:</P>
<PRE><FONT COLOR="#0066FF">AnsiString
S("Sam");
</FONT></PRE>
<P>This is the typical method you would use when initializing a variable of type
<TT>AnsiString</TT>.
<BLOCKQUOTE>
<P>
<HR>
<FONT COLOR="#000077"><B>NOTE: </B></FONT>I have included a second example program
called <TT>UsingAnsiString2</TT>, which features a small class that overrides all
the constructors for the <TT>AnsiString</TT> class:</P>
<PRE><FONT COLOR="#0066FF">class MyAnsiString : public AnsiString
{
public:
__fastcall MyAnsiString(void):
AnsiString() {}
__fastcall MyAnsiString(const char* src): AnsiString(src) {}
__fastcall MyAnsiString(const AnsiString& src): AnsiString(src) {}
__fastcall MyAnsiString(const char* src, unsigned char len): AnsiString (src, len) {}
__fastcall MyAnsiString(const wchar_t* src): AnsiString(src) {}
__fastcall MyAnsiString(char src): AnsiString(src) {}
__fastcall MyAnsiString(int src): AnsiString(src) {}
__fastcall MyAnsiString(double src): AnsiString(src) {}
};</FONT></PRE>
<P>This class is provided so you can step through the constructors to see which ones
are being called. For instance, the three constructors shown immediately after this
note have been rewritten in the UsingAnsiStrings2 program to
use <TT>MyAnsiStrings</TT>
rather than <TT>AnsiString</TT>s. This gives you an easy-to-use system for explicitly
testing which constructors are being called in which circumstance.<BR>
<BR>
When I come up with a unit like this that may be of some
general utility in multiple
programs, I usually put it in the <TT>utils</TT> subdirectory located on the same
level as the chapter subdirectories. In other words, I move or copy it out of the
directory where the files for the current program are
stored and place it in a subdirectory
called <TT>utils</TT> that is on the same level as the directories called <TT>Chap01</TT>,
<TT>Chap02</TT>, and so on. <BR>
<BR>
You might need to add this directory to the include search path for your
project,
or the program might not be able to find the <TT>MyAnsiString.h</TT> unit. To set
up the compiler for your system, go to Options | Project | Directories/Conditionals
and change the Include Path to point to the directory where
<TT>MyAnsiString.h</TT>
is stored.<BR>
<BR>
Sometimes I will leave one frozen copy of a unit in the directory where it was first
introduced and continue development of the copy of the unit that I place in the <TT>utils</TT>
subdirectory. That
way, you can find one copy of the file that looks the way you
expect it to look in the same directory as the program in which I introduce it, while
continuing to develop the code in a separate unit of the same name found in the <TT>utils</TT>
directory. Check the <TT>Readme.txt</TT> file on the CD that accompanies this book
for further information.
<HR>
</BLOCKQUOTE>
<P>The following are a few simple examples from the UsingAnsiStrings program that
show examples of creating and using
<TT>AnsiString</TT>s:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TForm1::PassinCString1Click(TObject *Sender)
{
AnsiString S("Sam");
Memo1->Text = S;
}
void __fastcall TForm1::PassInInteger1Click(TObject *Sender)
{
AnsiString MyNum(5);
Memo1->Text = MyNum;
}
void __fastcall TForm1::PassInaDouble1Click(TObject *Sender)
{
AnsiString MyDouble(6.6);
Memo1->Text = MyDouble;
}
</FONT></PRE>
<P>This code demonstrates several things. The first, and
most important point, is
that it shows how you can create an <TT>AnsiString</TT> object by initializing it
with a string, an integer, or a double. In short, these constructors can automatically
perform conversions for you. This means you can usually
write code like this:</P>
<PRE><FONT COLOR="#0066FF">AnsiString S;
int I = 4;
S = I;
ShowMessage(S);
</FONT></PRE>
<P>When working with C strings, you always have to be careful that the variable you
have been working with has been properly
initialized. This is not nearly as big a
concern when you are working with <TT>AnsiString</TT>s. Consider the following code:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TForm1::InitializetoZero1Click(TObject *Sender)
{
AnsiString S;
Memo1->Text = S.Length();
}
</FONT></PRE>
<P>The first line creates an <TT>AnsiString</TT> class that has a zero length string.
The second line performs a completely safe and legal call to one of the methods of
this <TT>AnsiString</TT>. This is
the type of situation that can be very dangerous
in C. Here, for instance, is some code that is likely to blow up on you:</P>
<PRE><FONT COLOR="#0066FF">char *S;
int i = strlen(S);
</FONT></PRE>
<P>This code appears to do more or less the same thing
as the code in the <TT>InitializetoZero1Click</TT>
method. In practice, however, this latter example actually raises an access violation,
while the <TT>AnsiString</TT> code example succeeds. The explanation is simply that
the declaration of the
<TT>AnsiString</TT> class created a real instance of an object
called <TT>S</TT>. You can safely call the methods of that object, even if the underlying
string has no memory allocated for it! The second example, however, leaves the <TT>char
*</TT>
declared in the first line completely impotent, with no memory allocated for
it. If you want to avoid trouble, you should not do anything with that variable until
you allocate some memory for it.</P>
<P>In the last few paragraphs I have outlined an
example illustrating what it is
I like about the <TT>AnsiString</TT> class. In short, this class makes strings safe
and easy to use.
<BLOCKQUOTE>
<P>
<HR>
<FONT COLOR="#000077"><B>NOTE: </B></FONT>It goes without saying that C strings are
generally faster than <TT>AnsiString</TT>s, and that they take up less memory. Clearly,
these are important features, and obviously I am not stating that C strings are now
obsolete.<BR>
<BR>
The reasoning on this issue is a bit like that we
undertake when deciding whether
to buy a car or a motorcycle. Cars are more expensive than motorcycles, and they
don't let you weave back and forth between lanes when there is congested traffic.
On the other hand, drivers of cars are much less
likely to end up in the hospital,
and cars are much more pleasant to be in during inclement weather. In the same way,
<TT>AnsiString</TT>s aren't as small and flexible as C strings, but they are less
likely to crash the system, and they stand up
better when you are in a rush or when
handled by inexperienced programmers.<BR>
<BR>
This book focuses on ways to quickly write safe, high-performance programs. If that
is your goal, use <TT>AnsiString</TT>s. If you are trying to write an
operating system,
a compiler, or the core module for a 3D game engine, you should probably concentrate
more on speed than I do in this book and should use <TT>AnsiString</TT>s only sparingly.<BR>
<BR>
Please note that my point here is not that you
can't use C++Builder to write highly
optimized code, but only that this book usually does not focus on that kind of project.
<HR>
</BLOCKQUOTE>
<H3><FONT COLOR="#000077">Sticky Constructor Issues</FONT></H3>
<P>The <TT>AnsiString</TT>
constructors are easy to use, but things can be a bit
confusing if you try to think about what is going on behind the scenes. For instance,
consider what happens when you pass an <TT>AnsiString</TT> to a function:</P>
<PRE><FONT
COLOR="#0066FF">AnsiString S;
MyFunc(S);
</FONT></PRE>
<P>If the call to <TT>MyFunc</TT> is by value, not by reference, the constructor
for <TT>S</TT> is going to be called each time you pass the string. This is not a
tremendous burden on your
program, but it is probably a bit more significant weight
than you had in mind to impose on your code. As a result, you should pass in the
address of the variable in most circumstances:</P>
<PRE><FONT COLOR="#0066FF">AnsiString S;
MyFunc(&S);
</FONT></PRE>
<P>Even better, you should construct methods that declare all their string variables
as being passed by reference:</P>
<PRE><FONT COLOR="#0066FF">int MyFunc(AnsiString &S)
{
S = "The best minds of my generation...";
return S.Length();
}
</FONT></PRE>
<P>This is like a <TT>var</TT> parameter in Object Pascal in that it lets you pass
the string by reference without worrying about pointers:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall
TForm1::Button1Click(TObject *Sender)
{
AnsiString S;
int i = MyFunc(S);
ShowMessage(S + " Length: " + i);
}
</FONT></PRE>
<P>Even when <TT>MyFunc</TT> changes the string, the result of the changes is reflected
in the calling
module. This syntax passes a pointer to the function, but makes it
seem as though you are working with a local stack-based variable on both the caller
and calling sides.</P>
<P>Consider the following code samples:</P>
<PRE><FONT
COLOR="#0066FF">MyAnsiString S = "The road to the contagious hospital";
MyAnsiString S1 = AnsiString("If I had a green automobile...");
MyAnsiString S2("All that came out of them came quiet, like the four seasons");
ShowMessage(S + `\r' + S1 + `\r' + S2);
</FONT></PRE>
<P>It should be clear from looking at this code that the second example will take
longer to execute than the third, because it calls two constructors rather than just
one. You might also think
that the first takes longer than the third. A logical course
of reasoning would be to suppose that, at minimum, it would have to call both a constructor
and the equals operator. In fact, when I stepped through the code, it became clear
that the
compiler simply called the constructor immediately in the first example,
and that the machine code executed for the first and third examples was identical.</P>
<P>What is the lesson to be learned here? Unless you are writing a compiler, an operating
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -