📄 ch24.htm
字号:
{
float * f = new float[10];
// use array
delete f; // Oops! Deleted f[0] correct version is delete [] f;
}
// pointer of new memory goes out of scope before delete
{
const char * DeleteP = "Don't forget P";
char * p = new char[strlen(DeleteP) + 1];
strcpy( p, DeleteP );
} // scope ended before delete[]
class A
{
public:
int * pi;
}
A::A()
{
pi = new int();
*pi = 3;
}
// ..later on, some code using this class..
A firsta; //allocates an int for first.pi to point to
B seconda; //allocates another int for seconda.pi
seconda=firsta;
// will perform a bitwise (shallow) copy. Both objects
// have a pi that points to the first int allocated.
// The pointer to the second int allocated is gone
</PRE>
<PRE>// forever.
</PRE>
<P>The code fragments all represent ways in which memory can be allocated and the
pointer to that memory lost before deallocation. After the pointer goes out of scope,
you can't reclaim the memory, and no one else can use it either. It's even worse
when you consider exceptions, discussed in Chapter 26, "Exceptions and Templates,"
because if an exception is thrown, your flow of execution may leave a function before
reaching the delete at the bottom of the code. Because destructors are called for
objects that are going out of scope as the stack unwinds, you can prevent some of
these problems by putting delete calls in destructors. This, too, is discussed in
more detail in Chapter 26, in the "Placing the catch Block" section.</P>
<P>Like all bugs, the secret to dealing with memory leaks is to prevent them--or
to detect them as soon as possible when they occur. You can develop some good habits
to help you:</P>
<P>
<UL>
<LI>If a class contains a pointer and allocates that pointer with new, be sure to
code a destructor that deletes the memory. Also, code a copy constructor and an operator
(=).
<P>
<LI>If a function will allocate memory and return something to let the calling program
access that memory, it must return a pointer instead of a reference. You can't delete
a reference.
<P>
<LI>If a function will allocate memory and then delete it later in the same function,
allocate the memory on the stack, if at all possible, so that you don't forget to
delete it.
<P>
<LI>Never change a pointer's value unless you have first deleted the object or array
it was pointing to. Never increment a pointer that was returned by new.
</UL>
<H3><A NAME="Heading7"></A>Debug new and delete</H3>
<P>MFC has a lot to offer the programmer who is looking for memory leaks. In debug
builds, whenever you use new and delete, you are actually using special debug versions
that track the filename and line number on which each allocation occurred and match
up deletes with their news. If memory is left over as the program ends, you get a
warning message in the output section, as shown in Figure 24.4.</P>
<P><A HREF="javascript:popUp('24uvc04.gif')"><B>FIG. 24.4</B></A><B> </B><I>Memory
leaks are detected automatically in debug builds.</I></P>
<P>To see this for yourself, create an AppWizard MDI application called Leak, accepting
all the defaults. In the InitInstance() function of the application class (CLeakApp
in this example), add this line:</P>
<P>
<PRE>int* pi = new int[20];
</PRE>
<P>Build a debug version of the application and run it by choosing Build, Start Debug,
and Go, or click the Go button on the Build minibar. You will see output like Figure
24.4. Notice that the filename (Leak.cpp) and line number where the memory was allocated
are provided in the error message. Double-click that line in the output window, and
the editor window displays Leak.cpp with the cursor on line 54. (The coordinates
in the lower-right corner always remind you what line number you are on.) If you
were writing a real application, you would now know what the problem is. Now you
must determine where to fix it (more specifically, where to put the delete).</P>
<P>
<H3><A NAME="Heading8"></A>Automatic Pointers</H3>
<P>When a program is executing within a particular scope, like a function, all variables
allocated in that function are allocated on the stack. The <I>stack</I> is a temporary
storage space that shrinks and grows, like an accordion. The stack is used to store
the current execution address before a function call, the arguments passed to the
function, and the local function objects and variables.</P>
<P>When the function returns, the <I>stack pointer</I> is reset to that location
where the prior execution point was stored. This makes the stack space after the
reset location available to whatever else needs it, which means those elements allocated
on the stack in the function are gone. This process is referred to as <I>stack unwinding.</I></P>
<BLOCKQUOTE>
<P>
<HR>
<strong>NOTE:</strong> Objects or variables defined with the keyword static are not allocated
on the stack. 
<HR>
</BLOCKQUOTE>
<P>Stack unwinding also happens when an exception occurs. To reliably restore the
program to its state before an exception occurred in the function, the stack is unwound.
Stack-wise variables are gone, and the destructors for stack-wise objects are called.
Unfortunately, the same is not true for dynamic objects. The handles (for example,
pointers) are unwound, but the unwinding process doesn't call delete. This causes
a memory leak.</P>
<P>In some cases, the solution is to add delete statements to the destructors of
objects that you know will be destructed as part of the unwinding, so they can use
these pointers before they go out of scope. A more general approach is to replace
simple pointers with a C++ class that can be used just like a pointer but contains
a destructor that deletes any memory at the location where it points. Don't worry,
you don't have to write such a class: One is included in the Standard Template Library,
which comes with Visual C++. Listing 24.2 is a heavily edited version of the auto_ptr
class definition, presented to demonstrate the key concepts.</P>
<BLOCKQUOTE>
<P>
<HR>
<strong>TIP:</strong> If you haven't seen template code before, it's explained in Chapter
26.
<HR>
</BLOCKQUOTE>
<H4>Listing 24.2  A Scaled-Down Version of the auto_ptr Class</H4>
<PRE> // This class is not complete. Use the complete definition in
//the Standard Template Library.
template <class T>
class auto_ptr
{
public:
auto_ptr( T *p = 0) : rep(p) {}
// store pointer in the class
~auto_ptr(){ delete rep; } // delete internal rep
// include pointer conversion members
inline T* operator->() const { return rep; }
inline T& operator*() const { return *rep; }
private:
T * rep;
</PRE>
<PRE> };
</PRE>
<P>The class has one member variable, a pointer to whatever type that you want a
pointer to. It has a one-argument constructor to build an auto_ptr from an int* or
a Truck* or any other pointer type. The destructor deletes the memory pointed to
by the internal member variable. Finally, the class overrides -> and *, the dereferencing
operators, so that dereferencing an auto_ptr feels just like dereferencing an ordinary
pointer.</P>
<P>If there is some class C to which you want to make an automatic pointer called
p, all you do is this:</P>
<P>
<PRE>auto_ptr<C> p(new C());
</PRE>
<P>Now you can use p as though it were a C*--for example:</P>
<P>
<PRE>p->Method(); // calls C::Method()
</PRE>
<P>You never have to delete the C object that p points to, even in the event of an
exception, because p was allocated on the stack. When it goes out of scope, its destructor
is called, and the destructor calls delete on the C object that was allocated in
the new statement.</P>
<P>You can read more about managed pointers and exceptions in Chapter 26.</P>
<P>
<H2><A NAME="Heading9"></A>Using Optimization to Make Efficient Code</H2>
<P>There was a time when programmers were expected to optimize their code themselves.
Many a night was spent arguing about the order in which to test conditions or about
which variables should be register instead of automatic storage. These days, compilers
come with optimizers that can speed execution or shrink program size far beyond what
a typical programmer can accomplish by hand.</P>
<P>Here's a simple example of how optimizers work. Imagine you have written a piece
of code like this:</P>
<P>
<PRE>for (i=0;i<10;i++)
{
y=2;
x[i]=5;
}
for (i=0; i<10; i++)
{
total += x[i];
}
</PRE>
<P>Your code will run faster, with no effect on the final results, if you move the
y=2 in front of the first loop. In addition, you can easily combine the two loops
into a single loop. If you do that, it's faster to add 5 to total each time than
it is to calculate the address of x[i] to retrieve the value just stored in it. Really
bright optimizers may even realize that total can be calculated outside the loop
as well. The revised code might look like this:</P>
<P>
<PRE>y=2;
for (i=0;i<10;i++)
{
x[i]=5;
}
total += 50;
</PRE>
<P>Optimizers do far more than this, of course, but this example gives you an idea
of what's going on behind the scenes. It's up to you whether the optimizer focuses
on speed, occasionally at the expense of memory usage, or tries to minimize memory
usage, perhaps at a slighter lower speed.</P>
<P>To set the optimization options for your project, select the Project, Settings
command from Developer Studio's menu bar. The Project Settings property sheet, first
shown in Figure 24.1, appears. Click the C/C++ tab and make sure you are looking
at the Release settings; then select Optimizations in the Category box. Keep optimization
turned off for debug builds because the code in your source files and the code being
executed won't match line for line, which will confuse you and the debugger. You
should turn on some kind of optimization for release builds. Choose from the drop-down
list box, as shown in Figure 24.5.</P>
<P><A HREF="javascript:popUp('24uvc05.gif')"><B>FIG. 24.5</B></A><B> </B><I>Select
the type of optimization you want.</I></P>
<P>If you select the Customize option in the Optimizations box, you can select from
the list of individual optimizations, including Assume No Aliasing, Global Optimizations,
Favor Fast Code, Generate Intrinsic Functions, Frame-Pointer Omission, and more.
However, as you can tell from these names, you really have to know what you're doing
before you set up a custom optimization scheme. For now, accept one of the schemes
that have been laid out for you.</P>
<P>
<H2><A NAME="Heading10"></A>Finding Bottlenecks by Profiling</H2>
<P>Profiling an application lets you discover <I>bottlenecks</I>, pieces of code
that are slowing your application's execution and deserve special attention. It's
pointless to hand-optimize a routine unless you know that the routine is called often
enough for its speed to matter.</P>
<P>Another use of a profiler is to see whether the test cases you have put together
result in every one of your functions being called or in each line of your code being
executed. You may think you have selected test inputs that guarantee this; however,
the profiler can confirm it for you.</P>
<P>Visual C++ includes a profiler integrated with the IDE: All you need to do is
use it. First, adjust your project settings to include profiler information. Bring
up the Project Settings property sheet as you did in the preceding section and click
the Link tab. Check the Enable Profiling check box. Click OK and rebuild your project.
Links will be slower now because you can't do an incremental link when you are planning
to profile, but you can go back to your old settings after you've learned a little
about the way your program runs. Choose Build, Profile and the Profile dialog box,
shown in Figure 24.6, appears.</P>
<P><A HREF="javascript:popUp('24uvc06.gif')"><B>FIG. 24.6</B></A><B> </B><I>A profiler
can gather many kinds of information.</I></P>
<P>If you aren't sure what any of the radio buttons on this dialog box mean, click
the question mark in the upper-right corner and then click the radio button. You'll
receive a short explanation of the option. (If you would like to add this kind of
context-sensitive Help to your own applications, be sure to read Chapter 11, "Help.")</P>
<P>You don't profile as a method to catch bugs, but it can help to validate your
testing or show you the parts of your application that need work, which makes it
a vital part of the developer's toolbox. Get in the habit of profiling all your applications
at least once in the development cycle.</P>
<H1></H1>
<CENTER>
<P>
<HR>
<A HREF="ch23.htm" tppabs="http://www.fintech.ru/library/prog/SEUsingVC6/ch23/ch23.htm"><IMG SRC="previous.gif" tppabs="http://www.fintech.ru/library/prog/SEUsingVC6/button/previous.gif" WIDTH="128" HEIGHT="28"
ALIGN="BOTTOM" ALT="Previous chapter" BORDER="0"></A><A HREF="ch25.htm" tppabs="http://www.fintech.ru/library/prog/SEUsingVC6/ch25/ch25.htm"><IMG
SRC="next.gif" tppabs="http://www.fintech.ru/library/prog/SEUsingVC6/button/next.gif" WIDTH="128" HEIGHT="28" ALIGN="BOTTOM" ALT="Next chapter"
BORDER="0"></A><A HREF="index.htm" tppabs="http://www.fintech.ru/library/prog/SEUsingVC6/index.htm"><IMG SRC="contents.gif" tppabs="http://www.fintech.ru/library/prog/SEUsingVC6/button/contents.gif" WIDTH="128"
HEIGHT="28" ALIGN="BOTTOM" ALT="Contents" BORDER="0"></A> <BR>
<BR>
</P>
<P>© <A HREF="copy.htm" tppabs="http://www.fintech.ru/library/prog/SEUsingVC6/copy.htm">Copyright</A>, Macmillan Computer Publishing. All
rights reserved.
</CENTER>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -