Discussion:
Polymorphism - accessing derived class data members
(too old to reply)
Alexh
2010-02-01 19:26:08 UTC
Permalink
Hi all,

I have implemented various graphic elements (classes) which are
derived from a base class. The primary advantage of this is that any
operations on the derived classes can be done via virtual functions
and thus the code that performs the operation does not have to
identify the element (avoids a huge switch statement for every
operation). This is typical polymorphism.

The problem I'm having is modification of existing elements. The
properties of the elements are set up from a dialog (specific to each
type of element) which executes a new statement to create the element.
Since the pointer for element is the base type (and not the derived
class type), it cannot be used to access the derived class variables
which is necessary if the user wants to modify the element options for
an existing element. Here is some basic code -

class CElement : public CObject //base class
{

//vars here

virtual void Draw(...) const {};


CElement() {};
}

class CDog: public CElement //derived class
{

//vars here

virtual void CDog::Draw(...);

CDog(const CDogDlg& DogDlg); //constructor
CDog () {}

};


//implementation code generating Dog
CDogDlg DogDlg;

if (DogDlg.DoModal == IDOK){

CElement* pElement = new CDog(DogDlg);

//save pElement to a CTypedPtrList
}

Later, a user may need to modify an existing Dog. Currently, I do this
by opening DogDlg from a modify menu and on IDOK I delete the
exisiting Dog and simultaneously create a new one from the new dialog
data (delete/replace is transparent to user). The problem arises when
there is more than one Dog because then the dialog may not reflect the
correct data for the selected element (dialog reflects last useage).
Ideally, I would like to have the dialog data reflect that actual data
associated with the Dog I am modifying.


I asked this question on Codeproject and the only possible solution I
got was to use a dynamic_cast operator. I don't necessarily think
there is anything wrong with this but I'd like to see if there are any
other solutuions.

One possibility I thought of is to generate a dialog for each element
so there is a one to one association between element and dialog. This
does not in itself allow the dialog to get data from the derived class
but it obviates the need since it will always retain the last
settings. However, it does not retain the info after a restore from
serialization. I suppose the dialog control states could be serialized
if the dialog pointers are also stored in a CTypedPtrList. Here would
be some possible code -

CDogDlg* pDogDlg = new CDogDlg;

if (pDogDlg->DoModal() == IDOK){

CElement* pElement = new CDog(DogDlg);

//save pElement and pDogDlg to a CTypedPtrList

}

Some other methods that were suggested to me which won't work -

1. Change base class pointer to a derived class pointer. i.e. CDog*
pElement = new .... defeats purpose of polymorphism
2. Define a virtual function that returns the dialog data - problem is
that there are other derived classes with different types of dialog
data (i.e. CCAt, CMouse, etc.). The virtual function prototype
definition has to be consistent for all derived classes.
3. Instantiate the dialog class inside the derived class. This is a
catch 22 since the derived class constructor requires the dialog data.



Thanks for any suggestions.
Igor Tandetnik
2010-02-01 19:48:58 UTC
Permalink
Post by Alexh
I have implemented various graphic elements (classes) which are
derived from a base class. The primary advantage of this is that any
operations on the derived classes can be done via virtual functions
and thus the code that performs the operation does not have to
identify the element (avoids a huge switch statement for every
operation). This is typical polymorphism.
The problem I'm having is modification of existing elements. The
properties of the elements are set up from a dialog (specific to each
type of element) which executes a new statement to create the element.
Since the pointer for element is the base type (and not the derived
class type), it cannot be used to access the derived class variables
which is necessary if the user wants to modify the element options for
an existing element.
If you know somehow that the base class pointer you hold is actually a pointer to a specific derived class, you can just cast it.
Post by Alexh
Later, a user may need to modify an existing Dog. Currently, I do this
by opening DogDlg from a modify menu and on IDOK I delete the
exisiting Dog and simultaneously create a new one from the new dialog
data (delete/replace is transparent to user). The problem arises when
there is more than one Dog because then the dialog may not reflect the
correct data for the selected element (dialog reflects last useage).
Ideally, I would like to have the dialog data reflect that actual data
associated with the Dog I am modifying.
How do you know that the currently selected element is actually a Dog, suitable for editing with DogDlg?
Post by Alexh
Some other methods that were suggested to me which won't work -
1. Change base class pointer to a derived class pointer. i.e. CDog*
pElement = new .... defeats purpose of polymorphism
Not at all. Code designed to work with generic CElement can continue to do that. Code that is specific to CDog (such as DogDlg) is very much justified in wanting a CDog* pointer. Nothing says you must lose type information immediately after creating a new instance.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Alexh
2010-02-01 20:09:25 UTC
Permalink
Post by Igor Tandetnik
Post by Alexh
I have implemented various graphic elements (classes) which are
derived from a base class. The primary advantage of this is that any
operations on the derived classes can be done via virtual functions
and thus the code that performs the operation does not have to
identify the element (avoids a huge switch statement for every
operation). This is typical polymorphism.
The problem I'm having is modification of existing elements. The
properties of the elements are set up from a dialog (specific to each
type of element) which executes a new statement to create the element.
Since the pointer for element is the base type (and not the derived
class type), it cannot be used to access the derived class variables
which is necessary if the user wants to modify the element options for
an existing element.
If you know somehow that the base class pointer you hold is actually a pointer to a specific derived class, you can just cast it.
Post by Alexh
Later, a user may need to modify an existing Dog. Currently, I do this
by opening DogDlg from a modify menu and on IDOK I delete the
exisiting Dog and simultaneously create a new one from the new dialog
data (delete/replace is transparent to user). The problem arises when
there is more than one Dog because then the dialog may not reflect the
correct data for the selected element (dialog reflects last useage).
Ideally, I would like to have the dialog data reflect that actual data
associated with the Dog I am modifying.
How do you know that the currently selected element is actually a Dog, suitable for editing with DogDlg?
There is a CString var in CElement, say CString ElementName;
Post by Igor Tandetnik
Post by Alexh
Some other methods that were suggested to me which won't work -
1. Change base class pointer to a derived class pointer. i.e. CDog*
pElement = new ....   defeats purpose of polymorphism
Not at all. Code designed to work with generic CElement can continue to do that. Code that is specific to CDog (such as DogDlg) is very much justified in wanting a CDog* pointer. Nothing says you must lose type information immediately after creating a new instance.
--
With best wishes,
    Igor Tandetnik
With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Thank you Igor, you are a scholar and a gentleman!
Igor Tandetnik
2010-02-01 20:53:25 UTC
Permalink
Post by Alexh
Post by Igor Tandetnik
Post by Alexh
Later, a user may need to modify an existing Dog. Currently, I do
this by opening DogDlg from a modify menu and on IDOK I delete the
exisiting Dog and simultaneously create a new one from the new
dialog data (delete/replace is transparent to user). The problem
arises when there is more than one Dog because then the dialog may
not reflect the correct data for the selected element (dialog
reflects last useage). Ideally, I would like to have the dialog
data reflect that actual data associated with the Dog I am
modifying.
How do you know that the currently selected element is actually a
Dog, suitable for editing with DogDlg?
There is a CString var in CElement, say CString ElementName;
That's a poor man's RTTI, so what you are doing is not much different from dynamic_cast. If you want to be ideologically pure and true to the ways of polymorphism, grasshopper, you'd have a virtual method in CElement, something like ShowEditDialog(CWnd* parent), whereby each derived class would show a dialog designed for this class. Naturally, since the class knows its own type, it can pass a strongly typed pointer (specifically, 'this' pointer) to its associated dialog.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Victor Bazarov
2010-02-01 20:03:15 UTC
Permalink
Post by Alexh
I have implemented various graphic elements (classes) which are
derived from a base class. The primary advantage of this is that any
operations on the derived classes can be done via virtual functions
and thus the code that performs the operation does not have to
identify the element (avoids a huge switch statement for every
operation). This is typical polymorphism.
The problem I'm having is modification of existing elements. The
properties of the elements are set up from a dialog (specific to each
type of element) which executes a new statement to create the element.
Since the pointer for element is the base type (and not the derived
class type), it cannot be used to access the derived class variables
which is necessary if the user wants to modify the element options for
an existing element. Here is some basic code -
class CElement : public CObject //base class
{
//vars here
virtual void Draw(...) const {};
CElement() {};
}
class CDog: public CElement //derived class
{
//vars here
virtual void CDog::Draw(...);
CDog(const CDogDlg& DogDlg); //constructor
CDog () {}
};
//implementation code generating Dog
CDogDlg DogDlg;
if (DogDlg.DoModal == IDOK){
CElement* pElement = new CDog(DogDlg);
//save pElement to a CTypedPtrList
}
Later, a user may need to modify an existing Dog. Currently, I do this
by opening DogDlg from a modify menu and on IDOK I delete the
exisiting Dog and simultaneously create a new one from the new dialog
data (delete/replace is transparent to user). The problem arises when
there is more than one Dog because then the dialog may not reflect the
correct data for the selected element (dialog reflects last useage).
It seems that you need to give the existing Dog to the DogDlg so the
dialog could extract the data from the object (instead of simply
generating a new one).
Post by Alexh
Ideally, I would like to have the dialog data reflect that actual data
associated with the Dog I am modifying.
Yes, that way it's certainly better. Generally, you might consider
creating a stand-alone "default Dog" when your dialog is invoked to
"create" a Dog, and pass the object to the same "edit the Dog" dialog.
And when editing an existing Dog, you need to create a copy of that Dog
before passing it to DogDlg. The decision to copy the resulting Dog
into the permanent storage (whether by creating a new one, or by copying
back into the existing one) should be made by the caller of the DogDlg -
if OK, store it, otherwise discard.
Post by Alexh
I asked this question on Codeproject and the only possible solution I
got was to use a dynamic_cast operator. I don't necessarily think
there is anything wrong with this but I'd like to see if there are any
other solutuions.
One possibility I thought of is to generate a dialog for each element
so there is a one to one association between element and dialog. This
does not in itself allow the dialog to get data from the derived class
but it obviates the need since it will always retain the last
settings. However, it does not retain the info after a restore from
serialization. I suppose the dialog control states could be serialized
if the dialog pointers are also stored in a CTypedPtrList. Here would
be some possible code -
CDogDlg* pDogDlg = new CDogDlg;
if (pDogDlg->DoModal() == IDOK){
CElement* pElement = new CDog(DogDlg);
//save pElement and pDogDlg to a CTypedPtrList
}
Some other methods that were suggested to me which won't work -
1. Change base class pointer to a derived class pointer. i.e. CDog*
pElement = new .... defeats purpose of polymorphism
You need to elaborate on that.
Post by Alexh
2. Define a virtual function that returns the dialog data - problem is
that there are other derived classes with different types of dialog
data (i.e. CCAt, CMouse, etc.). The virtual function prototype
definition has to be consistent for all derived classes.
Yes, that's probably not a good idea. You might end up with the base
class that has attributes of all derived classes, many mutually
exclusive, and you don't want to have to edit the base class when
creating a new derived class.
Post by Alexh
3. Instantiate the dialog class inside the derived class. This is a
catch 22 since the derived class constructor requires the dialog data.
I think that the caller of the DogDlg should do the dynamic_cast<Dog*>
and pass the pointer (if it's non-null) to DogDlg for editing. The
dialog will work with Dog only.

Alternatively, every object might know how it's to be edited, so you
basically ask the object (Dog, Cat, whatever) to invoke known to it
dialog and pass the proper object in. It might look like this:

Dog* pDog; // somehow set
...
if (bEditing) {
CElement *pNew = pDog->Edit(); // will clone internally
if (pNew)
pDog->copyAttributesFrom(pNew);

delele pNew;
}
else if (bCreating) {
CElement *pNew = Dog::createAndEdit(); // we know it's a Dog
if (pNew) {
// store the new element somewhere
}
}

That will require your CElement to have the 'Edit' virtual function
(overridden in every concrete descendant), and the 'copyAttributesFrom'
virtual function (this one can 'dynamic_cast' and/or do whatever it
likes). Every class that can create another instance should have the
'createAndEdit' static function...

Anyway, it seems that you need to do several more iterations in your
design effort. Good luck!

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Continue reading on narkive:
Loading...