15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


How to avoid dangling pointers with modeless objects

Roger Onslow -- Roger_Onslow@compsys.com.au
Friday, June 14, 1996

Environment: any MFC any OS

When using modeless dialogs, property sheets and frames,
one (usually) constructs the object with "new", then calls
a "Create" member to create the object and then either
kills it by calling "DestroyWindow" or waits for it to be
killed whens its parent is destroyed.

For these modeless objects, one also overrides
"PostNcDestroy" to do a "delete this" (ie. suicide).

However, your original pointer to the object (the one
returned by "new") is now left dangling, and has no way of
knowing that the object it points to has been deleted.

To work around this, one can use the following trick --

Keep a pointer to the pointer to the object in the object
itselft and clear it to NULL on destruction.

eg:

class CMyModelessThing : public CWhatever {
 CMyModelessThing** m_ppThis;
public:
 CMyModelessThing(CMyModelessThing**ppThis,...)
 : CWhatever(...)
 , m_ppThis(ppThis)
 {
  ...
 }
 ~CMyModelessThing() {
  if (m_ppThis) *m_ppThis = NULL;
 }
 ... // including PostNcDestroy
};

...

// in some class like CMainFrame...
 CMyModelessThing* m_pMyModelessThing;
...
// in some routin like CMainFrame::OnCreate()
 m_pMyModelessThing = new CMyModelessThing(&pThis);
 m_pMyModelessThing->Create(...)
 ...

Now, when the CMyModelessThing is destroyed,
m_pMyModelessThing is set to NULL, so there is no
dangling reference.


Roger Onslow
Senior Software Engineer
Computer Systems Australia
Ph: +61 49 577155
Fax: +61 49 765554
eMail: RogerO@compsys.com.au





Roger Onslow -- Roger_Onslow@compsys.com.au
Monday, June 17, 1996

Jerry Hewett writes...

>Talk about timing!  I'm in a weird situation with MFC, VC 4.0, and NT 3.51 
>that I've tried a variety of solutions for, and all of them make me 
>uncomfortable -- hoping that you might have an answer for me!
>
>I'm rather new to C++ (and *very* new to the MFC; I've only been using it for 
>three months now), so please bear with me if I'm missing the obvious.  I've 
>just finished writing an entire API (100+ function calls) using C++ that will 
>be used by other teams of programmers that are writing a slew of other C++ 
>applications to transfer and manipulate MPEG2 video on all sorts of fun 
>devices (network servers, Beta tape, Sony proprietary storage units, etc.) 
>via RS-422 and Ethernet connections.

Hmm.. an API (100+ function calls) using C++

Does this mean they are simply extern (global) functions rather
than in a class?

If they don't NNED to be in a class, they could just stay as
global functions.  If you want to group them, put them in a
namespace (in VC4.0 and later -- see online help)

But lets assume you want/need to put 'em in a class (good idea)
(makes it easy to create a .dll if they're in a class anyway).

>All these function calls do is convert parameters into Sony-specific binary 
>data strings that are passed to/from Sony hardware.  Quite literally an ASCII 
>to BCD translator with access to various ports -- not even a real need to 
>create an "object", per se.  All *I* need to do is figure out a way to make 
>these function calls easily available to everyone else, without going through 
>a slew of bizarre contortions! 8-)
>
>The options:
>
>1)  My first stab at the problem was to make the functions static:
>
>    class CSonyVS
>    {
>        public:
>            static BOOL VTRControl (_VTRINFO *);
>    }
>
>    so that the other programmers wouldn't have to do/remember anything
>    except:
>
>    CSonyVS::VTRControl (*VTR);
>
>    but I couldn't find any detailed information about what happens when
>    you create static methods in Windows/NT (for all I know, this could
>    end up locking the machine if memory needs to be moved/swapped!)

You seem confused about what "static" means.  All a static member
is, is a function which does not need a "this" pointer (ie, it
doesn't work implicitly on a particular object of the class) It
has nothing to do with memory allocation etc. Same for static
data member.

Static data and functions are EXACTLY the same as global data and
functions EXCEPT they are warpped up in the class, so you need to
put access modifier on (eg CSonyVS:: as you have done).

If this solution is (logically) possible then it *IS* the way to go.
Only possible, of course, if there is no per-instance member data.
(ie. you don't need any state info per CSonyVS object)

If you do nee per-object data, then the data should be made a
m_xxx members, and any functions that reference the data need to be
member functions (not statics).

You would also need to actually construct a CSonyVS object somwhere.
In that case, you would have a CSonyVS object as either:
a) a static member of other class(es)
b) a (per instance) member of other class(es)
c) a local var in another function/method

The choice of which to use depends on the how many separate
instances of the object are required.
if only one
 then make static.
if one per other class (like CEncodeVTR below
 then make a static CSonyVS c_Sony; member in the other classes
if one per instance of other class
 then make a CSonyVS m_sony; member in the other classes
if one per function call
 then make a CSonyVS aSony; local variable

>2)  Creating an instance of CSonyVS within the scope of each function:
>
>
>    CEncodeVTR::PlayBetaTape()
>    {
>        CSonyVS pSony;
>        _VTRINFO VTR;
>
>        pSony.VTRControl (*VTR);
>    }
>
>    This seems to work just fine, and the instance cleans up after itself
>    (?) once the function goes out of scope, so I plan to stick with this
>    method.  The only problem is that it's a royal pain in the ass to have
>    to create it in every bloody function. ;-)

NOTE: here, you could make a CSonyVS m_Sony; member of CEncodeVTR
so you don't need to "create it in every bloody function".

>3)  One of the lead programmers had a suggestion, but I tossed it out
>    because I felt it was *WAY* to damned complicated and obtuse.  It
>    involves creating a AfxGetApp() function that returns the pointer to
>    whatever I needed to access in the CSonyVS class, reams of code all
>    over the place, and convoluted syntax like:
>
>    GetMVPApp()->CSonyVS->VTRControl (*VTR);
>
>    Ee-yech!  Brian says all this garbage is necessary because Win/NT can
>    relocate my class anywhere in memory at any time, and this is the only
>    sure-fire way to make sure I'm getting a valid pointer to my function.
>    Me, I'm not so sure I buy into his explanation...

Ee-yech! indeed.  I think "Brian" is also mistaken on what
static means (see above).  I use statics with regularly no
worrys. You don't need to worry what NT virtual memory is
doing behind the scenes -- it's totally transparent.

It *is possible* to have data that can be logically moved
around in memory if you want -- but you have to ask NT to
allocate it with specail flags set, and you then get back
a handle (not a pointer).  To get a pointer to the data
you have to lock the handles, which freezes the location
until you release the lock, when it can be moved around
again.  HOWEVER, THIE HAS NOTHING TO DO WITH STATIC MEMBERS
and it is unlikely that you will need to delve into this
area.

Hope this clears things up (rather than make it less
clear).  Please eMail me if you wnat further clarification
or help...


Roger Onslow
Computer Systems Australia
etc.etc.





Niels Ull Jacobsen -- nuj@kruger.dk
Wednesday, June 19, 1996

At 12:19 17-06-96 EAT, you wrote:
>Jerry Hewett writes...
>
>>Talk about timing!  I'm in a weird situation with MFC, VC 4.0, and NT 3=
.51=20
>>that I've tried a variety of solutions for, and all of them make me=20
>>uncomfortable -- hoping that you might have an answer for me!
>>
>>I'm rather new to C++ (and *very* new to the MFC; I've only been using =
it for=20
>>three months now), so please bear with me if I'm missing the obvious.  =
I've=20
>>just finished writing an entire API (100+ function calls) using C++ tha=
t will=20
>>be used by other teams of programmers that are writing a slew of other =
C++=20
>>applications to transfer and manipulate MPEG2 video on all sorts of fun=
=20
>>devices (network servers, Beta tape, Sony proprietary storage units, et=
c.)=20
>>via RS-422 and Ethernet connections.
>
>Hmm.. an API (100+ function calls) using C++
>
>Does this mean they are simply extern (global) functions rather
>than in a class?
>
>If they don't NNED to be in a class, they could just stay as
>global functions.  If you want to group them, put them in a
>namespace (in VC4.0 and later -- see online help)
>
>But lets assume you want/need to put 'em in a class (good idea)
>(makes it easy to create a .dll if they're in a class anyway).

[lots of good explanation about design left out]

I've seen a good design idea recently. Put all the functions in a DLL. Ad=
d a
simple=20
class wrapper. Now, when an instance of the class is created, you
LoadLibrary() and=20
use GetProcAddress to get a pointer to the function.
If the function is almost always called, use GetProcAddress in the constr=
uctor.
If commonly called, cache the GetProcAddress. If rarely used,
just use GetProcAddress always.

The advantage is that you *don't* have to load the DLL when your applicat=
ion
is loaded,
which (if the DLL is large, but only used once in a while) can speed up t=
he
loading
a lot. And you can handle it gracefully if the DLL isn't found.

sample code: (all of this must go into the application which wants to use
the DLL)


typedef int (*PFooFuncType)(int); // pointer to function taking an return=
ing int

class CMyLibrary
{
      typedef int (*PFooFuncType)(int); // pointer to function taking an =
int
and returning int
                                        // for simplicity, all library
functions will have this type
      HMODULE m_hLib;
      PFooFuncType m_pFooAlwaysCalled;
      PFooFuncType m_pFooSometimesCalled;
  =20
   public:
      CMyLibrary();
      CMyLibrary();
  =20
      void* GetProc(LPCSTR name_or_ordinalnumber)
      {
           ASSERT(m_hLib);
           void* pFunc =3D GetProcAddress(m_hLib, name_or_ordinalnumber);
           ASSERT(pFunc !=3D NULL);
           return pFunc;
      };
=20
      // library functions
      int FooAlwaysCalled(int bar);
      int FooSometimesCalled(int bar);
      int FooSeldomCalled(int bar);
}

CMyLibrary::CMyLibrary()
  : m_hLib(LoadLibrary("MyLib.DLL")),
    m_pFooSometimesCalled(NULL)
{
   ASSERT(m_hLib);
   m_pFooAlwaysCalled =3D (PFooFuncType) GetProc("_FooAlwayasCalled");
   // Perhaps check DLL version?
}

CMyLibrary::~CMyLibrary()
{
   VERIFY(FreeLibrary(m_hLib));
};

int CMyLibrary::FooAlwaysCalled(int bar)
{
    return (*m_pFooAlwaysCalled) (bar);
}


int CMyLibrary::FooSometimesCalled(int bar)
{
    if (!m_pFooSometimesCalled)
       m_pFooSometimesCalled =3D (PFooFuncType) GetProc("_FooSometimesCal=
led");

    return (*m_pFooSometimesCalled) (bar);
}

int CMyLibrary::FooSeldomCalled(int bar)
{
    return (*(PFooFuncType) GetProc("_FooSeldomCalled")) (bar);
}

Niels Ull Jacobsen, Kr=FCger A/S (nuj@kruger.dk)
Everything stated herein is THE OFFICIAL POLICY of the entire Kruger=20
group and should be taken as legally binding in every respect.=20
Pigs will grow wings and fly.









| Вернуться в корень Архива |