RONSLOW
asked on
QUIZ: Using pointer to derived that is pointing to object of base class
When can one safely (if ever) do something like this (ignoring access specifiers for simplicity):
class A {
...
};
class B : public A {
...
void f(); // perhaps virtual void f();
};
A a;
B* pB = (B*)(&a);
pB->f();
I would suggest that, in general, this is not good C++ code.
Is it defined as being legal / implementation dependent / not recommended / undefined in the C++ standard?
However, in some cases, on some platforms, you may get away with it.
Under what conditions would this be "OK", and when would it not be (eg. when would doing so would cause real problems like invalid function calls or memory corruptions / overwrites / violations)?
class A {
...
};
class B : public A {
...
void f(); // perhaps virtual void f();
};
A a;
B* pB = (B*)(&a);
pB->f();
I would suggest that, in general, this is not good C++ code.
Is it defined as being legal / implementation dependent / not recommended / undefined in the C++ standard?
However, in some cases, on some platforms, you may get away with it.
Under what conditions would this be "OK", and when would it not be (eg. when would doing so would cause real problems like invalid function calls or memory corruptions / overwrites / violations)?
ASKER CERTIFIED SOLUTION
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
Out of curiousity, why do you ask?
ASKER
Just had a recent heated debate with jhance and mikeblas.
I was saying the GetDlgItem returns a pointer to a temporary CTempWnd object and so it is not really good practice to cast like this:
CComboBox* p = (CComboBox*)GetDlgItem(id) ;
because the object pointed to by p is NOT a CComboBox at all (but a CTempWnd). (I assume you are familiar with temporary window objects etc).
Although I agree that for SOME CWnd-derived classes and SOME member functions this is ok, Mike and jhance claimed you could cast the return result to ANY CWnd class (at least originally), and that I was overreacting in saying that was bad advice.
Not surprisingly, jhance quoted fom mike's book and mike got awarded the points for the question for "proving" my wrong.
I quoted CCheckListBox as a counter example (as it does inded have member data, and functions the dereference it), but that was ignored (not sure on what grounds).
Only chensu came to my defense.
See the discussion (I was very pissed off by the end of it all) at https://www.experts-exchange.com/jsp/qShow.jsp?ta=mfc&qid=10300760 if you are interested.
Just trying to reassure myself that I wasn't going crazy on this.
I was saying the GetDlgItem returns a pointer to a temporary CTempWnd object and so it is not really good practice to cast like this:
CComboBox* p = (CComboBox*)GetDlgItem(id)
because the object pointed to by p is NOT a CComboBox at all (but a CTempWnd). (I assume you are familiar with temporary window objects etc).
Although I agree that for SOME CWnd-derived classes and SOME member functions this is ok, Mike and jhance claimed you could cast the return result to ANY CWnd class (at least originally), and that I was overreacting in saying that was bad advice.
Not surprisingly, jhance quoted fom mike's book and mike got awarded the points for the question for "proving" my wrong.
I quoted CCheckListBox as a counter example (as it does inded have member data, and functions the dereference it), but that was ignored (not sure on what grounds).
Only chensu came to my defense.
See the discussion (I was very pissed off by the end of it all) at https://www.experts-exchange.com/jsp/qShow.jsp?ta=mfc&qid=10300760 if you are interested.
Just trying to reassure myself that I wasn't going crazy on this.
ASKER
Seems like you've agreed with me, and have come up with the same cases as me. I also added that if the function called do any RTTI checks (including the MFC checks on runtime class), that you could end up with a program ASSERTing.
I'll reject temporarily to await further comments .. but will award the points to you soon (I'll invite you to resubmit the answer). OK?
I'll reject temporarily to await further comments .. but will award the points to you soon (I'll invite you to resubmit the answer). OK?
Hi nietod.There is a question waiting for you at :
https://www.experts-exchange.com/jsp/qShow.jsp?ta=winprog&qid=10301293
https://www.experts-exchange.com/jsp/qShow.jsp?ta=winprog&qid=10301293
ASKER
Wyn: Should I bill you for advertising your question in mine :-).
Why did you post a comment to nietod here (I don't mind, really, just curious how you got here)?
And you didn't even say hello :-(
Why did you post a comment to nietod here (I don't mind, really, just curious how you got here)?
And you didn't even say hello :-(
>> (I assume you are familiar with
>> temporary window objects etc).
No. I can't find CTempWind in the help. I know MFC makes temporary objects to act as window objects for windows that it didn't make, like a windoe mentioned in a message's parameters, that sort of thing.
>> CComboBox* p = (CComboBox*)GetDlgItem(id) ;
The issuea are what does it really point to and how are they related, if at all.
Like if a take a pointer to an object A and convert it to an object B (which is unrelated) I can then convert it to A again and use it safely. Is that this case?
>> you could cast the return result to ANY CWnd class
Certainly not. You simply need to cast to a CWnd derived object that doesn't have data members you "need" In any case, in a guaranteed that you can "move about" in a class heirarchy correctly with c-style casts. To be absolutly save you need to use static_cast or dynamic_cast to move about in the heirarchy. (And static cast is safe only if the pointer/reference type is the actual type of the object. If the object is derived from that type, it may work incorrectly--that is what dynanic_cast is for.) However, in VC the base class and derived class objects do start at the same address (if there is not a virtual base) so C-stlye casts are safe in this case in VC.
>> temporary window objects etc).
No. I can't find CTempWind in the help. I know MFC makes temporary objects to act as window objects for windows that it didn't make, like a windoe mentioned in a message's parameters, that sort of thing.
>> CComboBox* p = (CComboBox*)GetDlgItem(id)
The issuea are what does it really point to and how are they related, if at all.
Like if a take a pointer to an object A and convert it to an object B (which is unrelated) I can then convert it to A again and use it safely. Is that this case?
>> you could cast the return result to ANY CWnd class
Certainly not. You simply need to cast to a CWnd derived object that doesn't have data members you "need" In any case, in a guaranteed that you can "move about" in a class heirarchy correctly with c-style casts. To be absolutly save you need to use static_cast or dynamic_cast to move about in the heirarchy. (And static cast is safe only if the pointer/reference type is the actual type of the object. If the object is derived from that type, it may work incorrectly--that is what dynanic_cast is for.) However, in VC the base class and derived class objects do start at the same address (if there is not a virtual base) so C-stlye casts are safe in this case in VC.
ASKER
CTempWnd is not a publicaly exposd class. It is an (almost) empty class derived from CWnd, and is the class of object created when MFC needs a temporary CWnd object.
FYI: Here is a quick summary of how this is used (in case you aren't familair)...
There is a temporary map of window handles -> pointers to CTempWnd's kept. There is also a permantent map of window handles -> pointers to actual CWnd-derived object.
Whenever MFC tries to map a window handle to a CWnd* pointer (eg in GetDlgItem), it first looks in the permanent map (those CWnd-derived objects that have been attached to a window handle) and returns a pointer to the real CWnd-derived object that is attached. If there isn't one, it looks in the temporary map and returns a pointer to the CTempWnd object used recently. And if there isn't one of those, it creates a CTempWnd object and adds it to the map and returns a pointer to it
(Phew).
So, for the example:
>> CComboBox* p = (CComboBox*)GetDlgItem(id) ;
We assume that one hasn't already attached a CComboBox object to the control (eg. by DDX_Control) otherwise we'd be using that object instead. So GetDlgItem would return a pointer to a 'temporary' CTempWnd. so we have
CTempWnd derived from CWnd
CComboBox derived from CWnd
Now .. a CComboBox class does not (currently) add any member vars, and although it has some extra virtula functions, we are probably not going to call them. So in this case it should "work".
However, if I use CCheckListBox instead, then we have
CTempWnd derived from CWnd
CCheckListBox derived from CListBox derived from CWnd
-and- CCheckListBox adds some member variables.
This is the sort of case where things would not behave correctly.
FYI: Here is a quick summary of how this is used (in case you aren't familair)...
There is a temporary map of window handles -> pointers to CTempWnd's kept. There is also a permantent map of window handles -> pointers to actual CWnd-derived object.
Whenever MFC tries to map a window handle to a CWnd* pointer (eg in GetDlgItem), it first looks in the permanent map (those CWnd-derived objects that have been attached to a window handle) and returns a pointer to the real CWnd-derived object that is attached. If there isn't one, it looks in the temporary map and returns a pointer to the CTempWnd object used recently. And if there isn't one of those, it creates a CTempWnd object and adds it to the map and returns a pointer to it
(Phew).
So, for the example:
>> CComboBox* p = (CComboBox*)GetDlgItem(id)
We assume that one hasn't already attached a CComboBox object to the control (eg. by DDX_Control) otherwise we'd be using that object instead. So GetDlgItem would return a pointer to a 'temporary' CTempWnd. so we have
CTempWnd derived from CWnd
CComboBox derived from CWnd
Now .. a CComboBox class does not (currently) add any member vars, and although it has some extra virtula functions, we are probably not going to call them. So in this case it should "work".
However, if I use CCheckListBox instead, then we have
CTempWnd derived from CWnd
CCheckListBox derived from CListBox derived from CWnd
-and- CCheckListBox adds some member variables.
This is the sort of case where things would not behave correctly.
->Wyn: Should I bill you for advertising your question in mine :-).
======
????
confusion grows.
My question???
->Why did you post a comment to nietod here (I don't mind, really, just curious how you got here)?
==========
I found that question exist there this morning but now it's deep night here.I think it should be valued.It's not my question.
->And you didn't even say hello :-(
Ohh , sorry ,really sorry.I dont even realize it.I just want to mention nietod because that question has been posted for a long time.
I' m sorry RONSLOW :(
Maybe I'm a little senseless and foolishly ,having nothing to do but pour out blah :-(
HELLO , RONSLOW (try to compensate)
:-)
Many apologies.....
Best Regards
W.Yinan
======
????
confusion grows.
My question???
->Why did you post a comment to nietod here (I don't mind, really, just curious how you got here)?
==========
I found that question exist there this morning but now it's deep night here.I think it should be valued.It's not my question.
->And you didn't even say hello :-(
Ohh , sorry ,really sorry.I dont even realize it.I just want to mention nietod because that question has been posted for a long time.
I' m sorry RONSLOW :(
Maybe I'm a little senseless and foolishly ,having nothing to do but pour out blah :-(
HELLO , RONSLOW (try to compensate)
:-)
Many apologies.....
Best Regards
W.Yinan
ASKER
Thanks Wyn.
I'm happy now :-) All is forgiven :-)
HELLO to you too.
Its getting late here too (Australia) .. almost 1:00am. I think my mind is wandering.
I'm happy now :-) All is forgiven :-)
HELLO to you too.
Its getting late here too (Australia) .. almost 1:00am. I think my mind is wandering.
I just read the original (cost me 20 points!) and I would say I agree with both sides. In general it is not a safe thing to do, but with MFC they are relying on certain compiler-specific features to make it safe--usually. I would be a little distressed about the fact that it isn't always safe, but the circumstances under which it is not safe is pretty clear. (One more argument not to use MFC though.) On the blass/jhance side, can you image writting MFC code that didn't make use of this assumption? It would be much harder. However, I don't like it. My own window class library doesn't ever require anything like that. Straight legitiate C++.
Wyn, thanks, I never saw it.
ASKER
Sorry about the 20 points.
Interesting disucssion though, I'm sure you'll admit.
BTW: In your library, do you ever have to get a pointer to a C++ object for a window from the handle? If so, how do _you_ do that? Just curious.
Also regarding difficulty of not doing it that way.
There are a couple of alternatives.
1) use DDX_Control so there is a member var or your dialog class of the appropraite class that is attached to the window handle. Then you just use it (realy easy)
2) do the subclassing of/to a member var (like DDX_Control does) yourself in the onInitDialog. eg.
3) temporarily attach an object to the handle, use it and kill it. eg something like
CComboBox combobox;
combobox.Attach(::GetDlgIt em(id,this ));
// do whatever with the combobox
combobox.Detach();
4) encapulate the access in a (template) class. I wrote this one:
template <class T> class CGetDlgItem {
public:
CGetDlgItem(int id, CWnd* pWnd)
: m_bUseTemp(false)
, m_pT(NULL)
{
HWND hwnd =NULL;
pWnd->GetDlgItem(id,&hwnd) ;
CWnd* pDlgItem = (T*)CWnd::FromHandlePerman ent(hwnd);
if (pDlgItem) {
ASSERT(pDlgItem->IsKindOf( m_tempT.Ge tRuntimeCl ass()));
m_pT = (T*)pDlgItem;
m_bUseTemp = false;
} else {
m_pT = &m_tempT;
m_pT->Attach(hwnd);
m_bUseTemp = true;
}
}
~CGetDlgItem() {
if (m_bUseTemp) {
m_pT->Detach();
}
}
operator T* () {
return m_pT;
}
T* operator-> () {
return m_pT;
}
private:
bool m_bUseTemp;
T* m_pT;
T m_tempT;
};
which you use like this
CGetDlgItem<CComboBox> p(id,this);
// use p
which is even easier (and safer) than
CComboBox* p = (CComboBox*)GetDlgItem(id) ;
// use p;
Thanks for the comments.
Interesting disucssion though, I'm sure you'll admit.
BTW: In your library, do you ever have to get a pointer to a C++ object for a window from the handle? If so, how do _you_ do that? Just curious.
Also regarding difficulty of not doing it that way.
There are a couple of alternatives.
1) use DDX_Control so there is a member var or your dialog class of the appropraite class that is attached to the window handle. Then you just use it (realy easy)
2) do the subclassing of/to a member var (like DDX_Control does) yourself in the onInitDialog. eg.
3) temporarily attach an object to the handle, use it and kill it. eg something like
CComboBox combobox;
combobox.Attach(::GetDlgIt
// do whatever with the combobox
combobox.Detach();
4) encapulate the access in a (template) class. I wrote this one:
template <class T> class CGetDlgItem {
public:
CGetDlgItem(int id, CWnd* pWnd)
: m_bUseTemp(false)
, m_pT(NULL)
{
HWND hwnd =NULL;
pWnd->GetDlgItem(id,&hwnd)
CWnd* pDlgItem = (T*)CWnd::FromHandlePerman
if (pDlgItem) {
ASSERT(pDlgItem->IsKindOf(
m_pT = (T*)pDlgItem;
m_bUseTemp = false;
} else {
m_pT = &m_tempT;
m_pT->Attach(hwnd);
m_bUseTemp = true;
}
}
~CGetDlgItem() {
if (m_bUseTemp) {
m_pT->Detach();
}
}
operator T* () {
return m_pT;
}
T* operator-> () {
return m_pT;
}
private:
bool m_bUseTemp;
T* m_pT;
T m_tempT;
};
which you use like this
CGetDlgItem<CComboBox> p(id,this);
// use p
which is even easier (and safer) than
CComboBox* p = (CComboBox*)GetDlgItem(id)
// use p;
Thanks for the comments.
>> BTW: In your library, do you ever have
>> to get a pointer to a C++ object for a window
>> from the handle? If so, how
>> do _you_ do that? Just curious.
I store a pointer to the window object in the window's extra data. No maps to search. (However there is no way to represent windows not created by the libm which has never been a problem and if it ever is, there are wasy to solve it.) The library client never "sees" any handles of any type which is one reason this problem never arrises (the lib is a heavy wrapper around windows.) All controls are derived from the same type, so any generic control procedure returns a pointer to the control base class and an dynamic_cast can be used to safely convert it to the appropriate type if needed.
>> 1) use DDX_Control so there is a member var
That would certainly be preferable. but--and I don't use MFC--it seems to me that this isn't perfect because it seems like in MFC your objects may not "live" as long as the windows they represent. i.e. in some cases you might create an object, use it to create a window, then the object is destroyed. When you need to work witht the window later, a new object will be created and ther you woudl have a problem. (That is never a problem in my library, the objects's are dynamically allocated and reference counted so as long as something is using the window object (an open window is considered using it) the window object exists.
>> 3) temporarily attach an object to the handle,
In a sense though, that just moves the problem. You don't know what the handle is "really" for. That is sort of the root of the problem. Well, maybe that is my anti-handle side showing. But it just seemed to me that the source of the problem is that MFC doesn't know what the handle is really for so it returns something generic, now you are assuming something in the conversion. But I guess you were making that same assumption in the original AND assuming that the data type conversion would work too, so I guess this is better.
>> to get a pointer to a C++ object for a window
>> from the handle? If so, how
>> do _you_ do that? Just curious.
I store a pointer to the window object in the window's extra data. No maps to search. (However there is no way to represent windows not created by the libm which has never been a problem and if it ever is, there are wasy to solve it.) The library client never "sees" any handles of any type which is one reason this problem never arrises (the lib is a heavy wrapper around windows.) All controls are derived from the same type, so any generic control procedure returns a pointer to the control base class and an dynamic_cast can be used to safely convert it to the appropriate type if needed.
>> 1) use DDX_Control so there is a member var
That would certainly be preferable. but--and I don't use MFC--it seems to me that this isn't perfect because it seems like in MFC your objects may not "live" as long as the windows they represent. i.e. in some cases you might create an object, use it to create a window, then the object is destroyed. When you need to work witht the window later, a new object will be created and ther you woudl have a problem. (That is never a problem in my library, the objects's are dynamically allocated and reference counted so as long as something is using the window object (an open window is considered using it) the window object exists.
>> 3) temporarily attach an object to the handle,
In a sense though, that just moves the problem. You don't know what the handle is "really" for. That is sort of the root of the problem. Well, maybe that is my anti-handle side showing. But it just seemed to me that the source of the problem is that MFC doesn't know what the handle is really for so it returns something generic, now you are assuming something in the conversion. But I guess you were making that same assumption in the original AND assuming that the data type conversion would work too, so I guess this is better.
ASKER
>>> 1) use DDX_Control so there is a member var
>That would certainly be preferable. but--and I don't use MFC--it seems to
>me that this isn't perfect because it seems like in MFC your objects may
>not "live" as long as the windows they represent.
MFC Cwd-derived object can be asked to Create a window (by calling Create). They are then attached to that window. Alternatively they can be asked to Attach themselves to an existing window. When the object is destructed, any attached window is destroyed. Similarly, if an attached window is destroyed, it is atomatically detached first so that the unattached object lives on.
An MFC object that is attached by using DDX_Control is a member of the dialog, so it won't get destructed until the dialog is destruected (ie. after the associated window is destroyed). So lifetime isn't a problem.
If anything the problem (if any) is with programmers mixing MFC and raw API methods. Often becuase they 'grew up' on the API. It is the mix of techniques that can cause difficulties (in particular mixing MFC dialog and control classes with the API technique of using GetDlgItem).
Anyway, as you don't use MFC and your library is probably much safer, faster and easier, it is not of any real concern to you, other than academic interest. .. just thought I'd (try to) clear it up as best I can manage at 1:30am :-)
>That would certainly be preferable. but--and I don't use MFC--it seems to
>me that this isn't perfect because it seems like in MFC your objects may
>not "live" as long as the windows they represent.
MFC Cwd-derived object can be asked to Create a window (by calling Create). They are then attached to that window. Alternatively they can be asked to Attach themselves to an existing window. When the object is destructed, any attached window is destroyed. Similarly, if an attached window is destroyed, it is atomatically detached first so that the unattached object lives on.
An MFC object that is attached by using DDX_Control is a member of the dialog, so it won't get destructed until the dialog is destruected (ie. after the associated window is destroyed). So lifetime isn't a problem.
If anything the problem (if any) is with programmers mixing MFC and raw API methods. Often becuase they 'grew up' on the API. It is the mix of techniques that can cause difficulties (in particular mixing MFC dialog and control classes with the API technique of using GetDlgItem).
Anyway, as you don't use MFC and your library is probably much safer, faster and easier, it is not of any real concern to you, other than academic interest. .. just thought I'd (try to) clear it up as best I can manage at 1:30am :-)
>> When the object is destructed, any attached
>> window is destroyed. Similarly, if an attached
>> window is destroyed, it is atomatically
>> detached first so that the unattached object lives on.
I was under the impression that the lifetime of the objects and the windows were unrelated. i.e. if the object is destroyed the window can still remain in existance. That is not true?
>> window is destroyed. Similarly, if an attached
>> window is destroyed, it is atomatically
>> detached first so that the unattached object lives on.
I was under the impression that the lifetime of the objects and the windows were unrelated. i.e. if the object is destroyed the window can still remain in existance. That is not true?
ASKER
Not when they a 'attached'. A bit like being handcuffed. If you are handcuffed together and your partner jumps out of a plane, then you both go. If you take the handcuffs off first, then you can jump separately.
That is basically the reason for the permanent map between window handles and CWnd-derived objects (when attached). It ensures a one-to-one mapping so that destruction/destroying are synchronised, or if one Detaches first, are independant.
NOTE: the destructor for a CWnd-derived object calls DestroyWindow. The last step fo destroying a window is a call to OnNcDestroy (WM_NCDESTROY) which calls Detach. That is how the magic happens.
That is basically the reason for the permanent map between window handles and CWnd-derived objects (when attached). It ensures a one-to-one mapping so that destruction/destroying are synchronised, or if one Detaches first, are independant.
NOTE: the destructor for a CWnd-derived object calls DestroyWindow. The last step fo destroying a window is a call to OnNcDestroy (WM_NCDESTROY) which calls Detach. That is how the magic happens.
Ronslow, I read this question and the one you refer to (the MFC GetDlgItem discussion).
I fully agree with you Ronslow, and as a fact, I always avoid using GetDlgItem (I consequently use DDX).
Although you're right, and there are indeed examples where GetDlgItem fails (for instance, if you write you're own control class, reusability you know :-), you cannot win such a discussion. Probably the MFC designers indeed did it on perpose (what wonders me is that mike brought that up as defence... I for myself would be ashamed to have to admit that I would use such techniques in my software), and yes, it works in most cases... But it is bad design.
In general there are many examples in MFC to be found that I think of as bad design (like CString, and all there containers, or even VC6 itself, that is ashamingly far away from the ANSI standard)...
But again this is not about the best possible software design, this is about profit and power... so you'll never win such a discussion based on technical points, and for the same reason, you'll never be invited to a MFC board meeting, because they're purpose is not to make the best design, but to sell as much as possible.... and they succeed! Where I work, using Microsoft's products is company policy...
Luc
I fully agree with you Ronslow, and as a fact, I always avoid using GetDlgItem (I consequently use DDX).
Although you're right, and there are indeed examples where GetDlgItem fails (for instance, if you write you're own control class, reusability you know :-), you cannot win such a discussion. Probably the MFC designers indeed did it on perpose (what wonders me is that mike brought that up as defence... I for myself would be ashamed to have to admit that I would use such techniques in my software), and yes, it works in most cases... But it is bad design.
In general there are many examples in MFC to be found that I think of as bad design (like CString, and all there containers, or even VC6 itself, that is ashamingly far away from the ANSI standard)...
But again this is not about the best possible software design, this is about profit and power... so you'll never win such a discussion based on technical points, and for the same reason, you'll never be invited to a MFC board meeting, because they're purpose is not to make the best design, but to sell as much as possible.... and they succeed! Where I work, using Microsoft's products is company policy...
Luc
Hi Ronslow,
For what it's worth, I agree with nietod and you on the 'safety' issue, though I'd add that even simple multiple inheritance is likely to break the cast.
Nothing to say about MFC, just that I hope I'll never have to use it...
For what it's worth, I agree with nietod and you on the 'safety' issue, though I'd add that even simple multiple inheritance is likely to break the cast.
Nothing to say about MFC, just that I hope I'll never have to use it...
ASKER
Seems to be dead topic now .. so I'll give you some points. Thanks.