Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / Managed C++ / December 2006

Tip: Looking for answers? Try searching our database.

New/delete vs. declare/garbage-collect

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Vincent Fatica - 15 Dec 2006 00:20 GMT
I noticed that if I give MY_STRUCT a simple c'tor and d'tor, say,

    MY_STRUCT::MY_STRUCT()
    {
        hEvent = CreateEvent(...);
    }
    MY_STRUCT::~MY_STRUCT
    {
        CloseHandle(hEvent);
    }

then using a local instance via simple declaration,

    INT my_function()
    {
        MY_STRUCT foo;
        // blah
        return 0;
    }

adds nearly 6KB (most in the .text segment) to my target DLL when compared to
using it via new/delete.  Can someone explain why that happens?  The example
above is just a little simplified.  The actual struct has as members a HANDLE, a
BOOL, and an OVERLAPPED but still the only initialization I want is for the
HANDLE.  Thanks!
Signature

- Vince

Vincent Fatica - 15 Dec 2006 03:16 GMT
Let me ask the same question another way (more to the point I hope).  I use an
ENUM_INFO (described below) struct like this:

    INT function()
    {
        ENUM_INFO EnumInfo;
        //blah
        EnumWindows(..., &EnumInfo);
        // blah
        return 0;
    }

The struct and its c'tor and d'tor look like this:

struct ENUM_INFO
{
    HANDLE hPipe;
    BOOL bGlobal;
    OVERLAPPED Overlapped;
    ENUM_INFO();
    ~ENUM_INFO();
};
ENUM_INFO::ENUM_INFO()
{
    Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    Overlapped.Offset = Overlapped.OffsetHigh = 0;
};
ENUM_INFO::~ENUM_INFO()
{
    //CloseHandle(Overlapped.hEvent);
};

Quite simply, when I uncomment the CloseHandle() call (no other changes
anywhere) in the d'tor, my target DLL increases in size by 6KB (most in the
.text segment).  There's nothing special about CloseHandle(); the same will be
observed with any WIN32 API function in its place(e.g., Beep() or Sleep()).
What's going on?

If I instantiate an ENUM_INFO using new/delete, I don't see the size increase.

Thanks.
Signature

- Vince

Carl Daniel [VC++ MVP] - 15 Dec 2006 05:12 GMT
> Let me ask the same question another way (more to the point I hope).
> I use an ENUM_INFO (described below) struct like this:
[quoted text clipped - 36 lines]
> If I instantiate an ENUM_INFO using new/delete, I don't see the size
> increase.

How many places in your code are you creating local variables of this type?
Every single one of them will entail adding code to the function epilog to
destroy the object that won't be there unless you explicitly delete it.
Probably more importantly, having a destructor will cause any function
containing the object to have an exception frame and exception handling code
generated for it if you're compiling with -GX, -EHa or -EHs (in other words,
if you have exception handling turned on - it's on by default in 2005).  In
exchange for that extra code size you code code will behave correctly in the
face of exceptions.

-cd
Vincent Fatica - 15 Dec 2006 05:54 GMT
>> Let me ask the same question another way (more to the point I hope).
>> I use an ENUM_INFO (described below) struct like this:
[quoted text clipped - 38 lines]
>
>How many places in your code are you creating local variables of this type?

Only one place.

>Every single one of them will entail adding code to the function epilog to
>destroy the object that won't be there unless you explicitly delete it.
[quoted text clipped - 4 lines]
>exchange for that extra code size you code code will behave correctly in the
>face of exceptions.

Well, it looks like compiler madness to me.  If I make things a bit more
complicated, like this:

ENUM_INFO::ENUM_INFO(BOOL boo, HANDLE hoo)
{
    bGlobal = boo;
    hPipe = hoo;
    Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    Overlapped.Offset = Overlapped.OffsetHigh = 0;
};

ENUM_INFO::~ENUM_INFO()
{
    CloseHandle(Overlapped.hEvent);
    CloseHandle(hPipe);
};

And use it as a local variable like this:

ENUM_INFO EnumInfo(stristr(psz, L"/G") ? TRUE : FALSE,    
    CreateNamedPipe(szPipeName, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
        PIPE_WAIT, 1, 0, sizeof(EVENT), 2000, NULL));

My DLL drops the 6KB it gained from the set-up mentioned above.
Signature

- Vince

Vincent Fatica - 15 Dec 2006 06:09 GMT
>>> Let me ask the same question another way (more to the point I hope).
>>> I use an ENUM_INFO (described below) struct like this:
[quoted text clipped - 74 lines]
>
>My DLL drops the 6KB it gained from the set-up mentioned above.

Whereas (here's the real corker) if, in the code just above, I instantiate it
thus:

    ENUM_INFO EnumInfo(FALSE, NULL);

(regardless of whether I later set the real (desired) values of hPipe and
bGlogal) the DLL size jumps back up 6KB.  BTW, it's a release build.
Signature

- Vince

Arnaud Debaene - 15 Dec 2006 07:05 GMT
>>ENUM_INFO::ENUM_INFO(BOOL boo, HANDLE hoo)
>>{
[quoted text clipped - 26 lines]
> (regardless of whether I later set the real (desired) values of hPipe and
> bGlogal) the DLL size jumps back up 6KB.  BTW, it's a release build.

It's hard to be sure of anything without a complete sample (complete code +
compiler settings + compiler version), but I would say that when you see the
6 Kb increase, it's because the compiler decides that you need exception
safety mechanisms : that is, you've got a local (stack) variable with a
non-trivial destructor. In this situation, in order to be exception-safe,
the compiler needs to add an exception frame to the function where this
variable is declared, and it needs to add all it's internal machinery that
is used during stack-unwinding (eg, "catch" handlers tables, with filters by
exception types....) The stack-unwinding machinery is quite complex and need
a lot of static data (therefore the .text section increase).

However, I bet most of the 6 Ko increase is to be paid only once (that is,
if you add a second function that also allocates an ENUM_INFO on the stack,
you will see a much smaller increase).

Arnaud
MVP - VC
Vincent Fatica - 15 Dec 2006 07:33 GMT
>>>ENUM_INFO::ENUM_INFO(BOOL boo, HANDLE hoo)
>>>{
[quoted text clipped - 37 lines]
>exception types....) The stack-unwinding machinery is quite complex and need
>a lot of static data (therefore the .text section increase).

I don't know enough to say whether that's likely or not.  But IMHO,

    ENUM_INFO EnumInfo(FALSE, NULL);

(which causes the 6KB increase) looks safer than

    ENUM_INFO EnumInfo(stristr(psz, L"/G") ? TRUE : FALSE,
        CreateNamedPipe(szPipeName, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
        PIPE_WAIT, 1, 0, sizeof(EVENT), 2000, NULL));

which doesn't.
Signature

- Vince

Vincent Fatica - 15 Dec 2006 16:58 GMT
>>>>ENUM_INFO::ENUM_INFO(BOOL boo, HANDLE hoo)
>>>>{
[quoted text clipped - 49 lines]
>
>which doesn't.

FWIW, I'm building my DLL with VS2002/SP1.  Ths settings are:

/O1 /Ob1 /Os /Oy /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_USRDLL" /D
"EVENT2_EXPORTS" /D "_UNICODE" /D "UNICODE" /D "_WINDLL" /GF /FD /EHsc /ML /Gy
/Zc:wchar_t /Fo"Release/" /Fd"Release/vc70.pdb" /W3 /nologo /c /Wp64 /Zi /TP

/OUT:"Release/ev.dll" /INCREMENTAL:NO /NOLOGO /DLL /DEF:"event.def"
/SUBSYSTEM:WINDOWS /OPT:REF /OPT:ICF /OPT:NOWIN98 /IMPLIB:"Release/event2.lib"
/MACHINE:IX86 g:\Projects\event2\release\\takecmd.lib  kernel32.lib user32.lib
gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib
oleaut32.lib uuid.lib odbc32.lib odbccp32.lib release\TakeCmd.lib
Signature

- Vince

Ben Voigt - 15 Dec 2006 17:24 GMT
> On Fri, 15 Dec 2006 08:05:28 +0100, "Arnaud Debaene"
> <adebaene@club-internet.fr>
[quoted text clipped - 23 lines]
> CreateNamedPipe(szPipeName, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
> PIPE_WAIT, 1, 0, sizeof(EVENT), 2000, NULL));

Not from the compiler's point of view, because in the second case, all the
real work is done *before* calling the constructor.  Any exceptions will
cause the object not to be constructed, and therefore not need destruction.

This extra work for exception handling is a very good thing.  With stack
objects, you are guaranteed to (try to) free the kernel objects if an
exception is thrown after the object is created.  In fact, you should deploy
the RAII idiom completely, so that every kernel object is in a separate
variable that knows to free it.  These can all be member variables of
ENUM_INFO if you want.  But, let's look at your code.

If the CreateEvent fails in the constructor, you may never CloseHandle on
the pipe.  You don't try to use the event handle in the constructor, so the
object becomes live.  Then, when you try to use the event handle
(WaitForMultipleObjects perhaps) you could get an access violation.  The
compiler will begin stack unwinding, and call your destructor.  The
destructor call to CloseHandle the event fails with another access
violation, immediately leaving the destructor.  The pipe would not be
closed.

Using individual RAII objects will cause them to be separately destructed,
even if other destructors fail.

Getting all this right using new/delete would be:
(1) a big pain
(2) the __try/__finally logic would probably add the same 6kb as letting the
compiler use unwinding.

If you are willing to accept process termination in case of any failure
whatsoever, then disable exceptions.  Otherwise, appreciate that the 6kb is
necessary and generated by the compiler so you don't have to do it by hand.

> which doesn't.
Vincent Fatica - 15 Dec 2006 18:00 GMT
>But, let's look at your code.
>
[quoted text clipped - 6 lines]
>violation, immediately leaving the destructor.  The pipe would not be
>closed.

Thanks Ben.  I wasn't worrying much about catching errors when trying to sort
out the 6KB thing.  I have this sort of thing now (paraphrased)

FOO::FOO()
{
    handle1 = ... // NULL on fail
}
FOO:~FOO()
{
    if (handle1) CloseHandle(handle1);
}

code:

    FOO foo;
    if ( !foo.handle1 ) ... // don't do anything rash
    else ... // life is right
Signature

- Vince

Vincent Fatica - 15 Dec 2006 17:46 GMT
>It's hard to be sure of anything without a complete sample (complete code +
>compiler settings + compiler version), but I would say that when you see the
[quoted text clipped - 6 lines]
>exception types....) The stack-unwinding machinery is quite complex and need
>a lot of static data (therefore the .text section increase).

Well, Arnaud, that seems very likely.  When I remove /EHsc I can no longer cause
the size increase.  But I must repeat that whether or not the compiler decides
to add that code is quite serendipitous.  In the latest version, with the most
the most going on in the c'tor and d'tor (below), the safety mechanisms are not
added.

ENUM_INFO::ENUM_INFO(BOOL bg)
{
    WCHAR szPipeName[32];
    Sprintf(szPipeName, L"%s%lu", szPipeNameStub, g.dwPid);
    hPipe = CreateNamedPipe(szPipeName, PIPE_ACCESS_INBOUND |
        FILE_FLAG_OVERLAPPED, PIPE_WAIT, 1, 0, sizeof(EVENT), 2000, NULL);
    Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    Overlapped.Offset = Overlapped.OffsetHigh = 0;
    bGlobal = bg;
};

ENUM_INFO::~ENUM_INFO()
{
    if ( Overlapped.hEvent ) CloseHandle(Overlapped.hEvent);
    if ( hPipe ) CloseHandle( hPipe );
};

INT WINAPI function(WCHAR *psz)
{
    // blah
    // instantiation
    ENUM_INFO EnumInfo(stristr(psz, L"/G") ? TRUE : FALSE);
    if ( !EnumInfo.hPipe || !EnumInfo.Overlapped.hEvent )
        return -1; // do more?
    EnumWindows(..., &EnumInfo);
    // blah
    return 0;
}
Signature

- Vince


Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.