Solved

QUIZ: Using pointer to derived that is pointing to object of base class

Posted on 2000-02-26
20
263 Views
Last Modified: 2012-05-04
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)?

0
Comment
Question by:RONSLOW
  • 9
  • 7
  • 2
  • +2
20 Comments
 
LVL 22

Accepted Solution

by:
nietod earned 50 total points
ID: 2560816
It is certainly not safe in two places.

First of all a c-style cast like

B* pB = (B*)(&a);

only converts the type of the pointer, not what it actually points to.  This is not necessarily correct.  (especially if virtual base classes are involved, which is not the case here, but there's still not guarantee that bass and derived classes use pointers that are at the same address.)  The only correct way to do this is with a static_cast or a dynamic_cast.   If there are  virtual functions the dynamic_cast will return null, which doesn't help.  If there are no virtual function it is done like a static_cast.  And if a static_cast is done, the pointer will be adjusted to what would be the right address.  unfortunately, you don't know what is at the address so its hardly a safe thing to do.

But on most (all?) platforms assuming there are not virtual base classes, this is a safe thing to do because the base and derived classes will both start at the same address.

Next consider
pB->f();

Obviously if f() uses any "features" data members declared in B (that are not inherited from A) or uses any virtual member functions that are declared in B this will definitely be...bad.  However, assuming that the C-stly/static_cast above worked (that the two classes begin at the same address) then if f() restricts its self to accessig only A's data members and calling functions that aso only use A's data members (functions must be non-virtual and or must be declared virtual in A) then this will be safe on most platforms.     (Note f() could probably access static data members of B safely.)

>> when would doing so would cause real
>> problems like invalid function calls or
>> memory corruptions / overwrites / violations)?
If the object is structured in such a way that the two class's don't start at the same address.  Usually this only happens if there is a virtual base class.

If f() is virtual and declared in B (and not A) or if it calls any virtual function declared in B and not A.

If f() or the functions it calls  uses any non-static member data of B.
0
 
LVL 22

Expert Comment

by:nietod
ID: 2560817
Out of curiousity, why do you ask?
0
 
LVL 10

Author Comment

by:RONSLOW
ID: 2560829
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 http://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.
0
 
LVL 10

Author Comment

by:RONSLOW
ID: 2560835
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?

0
 
LVL 5

Expert Comment

by:Wyn
ID: 2560849
Hi nietod.There is a question waiting for you at :
http://www.experts-exchange.com/jsp/qShow.jsp?ta=winprog&qid=10301293
0
 
LVL 10

Author Comment

by:RONSLOW
ID: 2560858
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 :-(
0
 
LVL 22

Expert Comment

by:nietod
ID: 2560859
>> (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.
0
 
LVL 10

Author Comment

by:RONSLOW
ID: 2560879
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.

0
 
LVL 5

Expert Comment

by:Wyn
ID: 2560886
->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
0
 
LVL 10

Author Comment

by:RONSLOW
ID: 2560891
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.


0
Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

 
LVL 22

Expert Comment

by:nietod
ID: 2560894
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++.
0
 
LVL 22

Expert Comment

by:nietod
ID: 2560896
Wyn, thanks, I never saw it.
0
 
LVL 10

Author Comment

by:RONSLOW
ID: 2560903
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(::GetDlgItem(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::FromHandlePermanent(hwnd);
        if (pDlgItem) {
            ASSERT(pDlgItem->IsKindOf(m_tempT.GetRuntimeClass()));
            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.
0
 
LVL 22

Expert Comment

by:nietod
ID: 2560937
>> 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.
0
 
LVL 10

Author Comment

by:RONSLOW
ID: 2560957
>>> 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 :-)

0
 
LVL 22

Expert Comment

by:nietod
ID: 2560966
>> 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?
0
 
LVL 10

Author Comment

by:RONSLOW
ID: 2560997
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.

0
 
LVL 3

Expert Comment

by:LucHoltkamp
ID: 2564298
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
0
 
LVL 7

Expert Comment

by:KangaRoo
ID: 2564311
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...
0
 
LVL 10

Author Comment

by:RONSLOW
ID: 2621586
Seems to be dead topic now .. so I'll give you some points.  Thanks.
0

Featured Post

What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

Join & Write a Comment

Errors will happen. It is a fact of life for the programmer. How and when errors are detected have a great impact on quality and cost of a product. It is better to detect errors at compile time, when possible and practical. Errors that make their wa…
IntroductionThis article is the second in a three part article series on the Visual Studio 2008 Debugger.  It provides tips in setting and using breakpoints. If not familiar with this debugger, you can find a basic introduction in the EE article loc…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

759 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now