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.
| Вернуться в корень Архива |