OOP design problem with dynamic_cast

  • Thread starter Leslaw Bieniasz
  • Start date
L

Leslaw Bieniasz

Hello,

I have the following OOP design problem. I have a base class
designed as an element of a list, let's say:

class A
{
public:
// some stuff

A* Next;
A* Prev;
}

in which pointers Next and Prev serve for accessing next and
previous elements in the list. For simplicity, I declare here all
contents as public.

Class A is abstract. In practice, real lists are constructed using
various kinds of derived classes, for example:


class B : public A
{
public:
// some stuff

B_Data *Data;
void foo(void);
}


where B_Data is some data specific to class B, and method foo
serves for operations on Data. However, I need to be able to access
in foo not only the data in a given object, but also in other objects
in the list. Thus, I need to do something like this:

void B::foo(void)
{
B* bnext = dynamic_cast<B*>(Next);
B* bprev = dynamic_cast<B*>(Prev);

Data = bnext->Data + bprev->Data; // for example

// etc.
}


My question is: Is there any neat possibility for redesigning
the above construction, in order to avoid dynamic casting in foo?
Dynamic casting is quite expensive and regarded as not elegant.
On the other hand, I don't want to make Data available already in A,
because I have several different kinds of derived classes B, which differ
somewhat by the behaviour, and by the data they hold.
The above seems to be a common problem in situations when
some of the functionality is shared by the classes, so that making
a common base A is reasonable, but some other functionality is not shared.

L. B.


*-------------------------------------------------------------------*
| Dr. Leslaw Bieniasz, |
| Institute of Physical Chemistry of the Polish Academy of Sciences,|
| Department of Electrochemical Oxidation of Gaseous Fuels, |
| ul. Zagrody 13, 30-318 Cracow, Poland. |
| tel./fax: +48 (12) 266-03-41 |
| E-mail: (e-mail address removed) |
*-------------------------------------------------------------------*
| Interested in Computational Electrochemistry? |
| Visit my web site: http://www.cyf-kr.edu.pl/~nbbienia |
*-------------------------------------------------------------------*
 
B

benben

Leslaw said:
Hello,

I have the following OOP design problem. I have a base class
designed as an element of a list, let's say:

class A
{
public:
// some stuff

A* Next;
A* Prev;
}

Don't forget the semicolon.
in which pointers Next and Prev serve for accessing next and
previous elements in the list. For simplicity, I declare here all
contents as public.

Class A is abstract. In practice, real lists are constructed using
various kinds of derived classes, for example:

Well, you didn't make A abstract, did you?
class B : public A
{
public:
// some stuff

B_Data *Data;
void foo(void);
}
(semicolon)



where B_Data is some data specific to class B, and method foo
serves for operations on Data. However, I need to be able to access
in foo not only the data in a given object, but also in other objects
in the list. Thus, I need to do something like this:

void B::foo(void)
{
B* bnext = dynamic_cast<B*>(Next);
B* bprev = dynamic_cast<B*>(Prev);

Data = bnext->Data + bprev->Data; // for example

// etc.
}


My question is: Is there any neat possibility for redesigning
the above construction, in order to avoid dynamic casting in foo?
Dynamic casting is quite expensive and regarded as not elegant.
On the other hand, I don't want to make Data available already in A,
because I have several different kinds of derived classes B, which differ
somewhat by the behaviour, and by the data they hold.
The above seems to be a common problem in situations when
some of the functionality is shared by the classes, so that making
a common base A is reasonable, but some other functionality is not shared.

I would offer two simple alternatives.

1) Use std::list from the standard library and get rid of A:

class B
{
public: void bar();
};

template <typename Itr>
void foo(Itr p)
{
Itr prev = p - 1;
Itr next = p + 1;

// play around *p, *prev and *next...
}

std::list<B> ls;
ls.push_back(B());
ls.push_back(B());
std::for_each(ls.begin(), ls.end(), std::mem_fun(B::bar));

2) Make class A a class template:

template <typename T>
class A
{
public: // note: not encapsulating...
T* prev;
T* next;
T val;

template <typename U>
A(T* p, T* n, const U& u):val(u){}

virtual ~A(){}
};

class B
{
public:
void bar();
};

void foo(A<B>& node)
{
B& curr = node.val;
B& prev = node.prev->val;
B& next = ndoe.next->val;

// play around curr, prev and next
}

IMHO you should be more cautious when using inheritance. Generally this
kind of data structuring doesn't not fit into the domain of OOP so B
should not inherit from A, never.

Regards,
Ben
 
B

benben

template <typename T>
class A
{
public: // note: not encapsulating...
T* prev;
T* next;
T val;

template <typename U>
A(T* p, T* n, const U& u):val(u){}

virtual ~A(){}

Sorry, please ignore the line above. A's destructor should not be virtual.

Ben
 
D

Daniel T.

Leslaw Bieniasz said:
Hello,

I have the following OOP design problem. I have a base class
designed as an element of a list, let's say:

class A
{
public:
// some stuff

A* Next;
A* Prev;
}

in which pointers Next and Prev serve for accessing next and
previous elements in the list. For simplicity, I declare here all
contents as public.

Class A is abstract. In practice, real lists are constructed using
various kinds of derived classes, for example:


class B : public A
{
public:
// some stuff

B_Data *Data;
void foo(void);
}


where B_Data is some data specific to class B, and method foo
serves for operations on Data. However, I need to be able to access
in foo not only the data in a given object, but also in other objects
in the list. Thus, I need to do something like this:

void B::foo(void)
{
B* bnext = dynamic_cast<B*>(Next);
B* bprev = dynamic_cast<B*>(Prev);

Data = bnext->Data + bprev->Data; // for example

// etc.
}


My question is: Is there any neat possibility for redesigning
the above construction, in order to avoid dynamic casting in foo?

First, don't do it. Base classes with member-variables tend to be
brittle. And at any rate, is there *any* context where some client uses
one or more "A" objects without caring what sub-type they are?
Dynamic casting is quite expensive and regarded as not elegant.
On the other hand, I don't want to make Data available already in A,
because I have several different kinds of derived classes B, which differ
somewhat by the behaviour, and by the data they hold.

Use templates instead.
The above seems to be a common problem in situations when
some of the functionality is shared by the classes, so that making
a common base A is reasonable, but some other functionality is not shared.

You probably should be asking all this in the newsgroup "comp.object"
 
B

benben

Leslaw said:
>
> Hello,
>
> I have the following OOP design problem. I have a base class
> designed as an element of a list, let's say:
>
> class A
> {
> public:
> // some stuff
>
> A* Next;
> A* Prev;
> }

Don't forget the semicolon.
>
> in which pointers Next and Prev serve for accessing next and
> previous elements in the list. For simplicity, I declare here all
> contents as public.
>
> Class A is abstract. In practice, real lists are constructed using
> various kinds of derived classes, for example:

Well, you didn't make A abstract, did you?
>
>
> class B : public A
> {
> public:
> // some stuff
>
> B_Data *Data;
> void foo(void);
> }
(semicolon)

>
>
> where B_Data is some data specific to class B, and method foo
> serves for operations on Data. However, I need to be able to access
> in foo not only the data in a given object, but also in other objects
> in the list. Thus, I need to do something like this:
>
> void B::foo(void)
> {
> B* bnext = dynamic_cast<B*>(Next);
> B* bprev = dynamic_cast<B*>(Prev);
>
> Data = bnext->Data + bprev->Data; // for example
>
> // etc.
> }
>
>
> My question is: Is there any neat possibility for redesigning
> the above construction, in order to avoid dynamic casting in foo?
> [snip]

I would offer two simple alternatives.

1) Use std::list from the standard library and get rid of A:

// note: code not tested

class B
{
public: void bar();
};

std::list<B> ls;
ls.push_back(B());
ls.push_back(B());
std::for_each(ls.begin(), ls.end(), std::mem_fun(B::bar));

2) Make class A a class template:

// Note: code not tested

template <typename T>
class A
{
public: // note: not encapsulating...
T* prev;
T* next;
T val;

template <typename U>
A(T* p, T* n, const U& u):val(u){}

virtual ~A(){}
};

class B
{
public:
void bar();
};

void foo(A<B>& node) // example
{
A<B>* p = &node;

while (p != 0)
{
p->val.bar();
p = p->next;
}
}

IMHO you should be more cautious when using inheritance. Generally this
kind of data structuring doesn't not fit into the domain of OOP so B
should not inherit from A, never.
>
> L. B.

Regards,
Ben
 
N

Noah Roberts

Leslaw said:
My question is: Is there any neat possibility for redesigning
the above construction, in order to avoid dynamic casting in foo?

Yes, in that you could completely rewrite it - you can call that
'redesign'. No, you can't get rid of the dynamic cast the way you are
going.

http://www.dcs.bbk.ac.uk/~keith/javacrash/Inheritance.pdf

DO NOT inherit for reuse purposes. Inherit when you have to because
you are actually that kind of object and you need to do some overriding
of virtual functions.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top