OOP design problem with dynamic_cast

Discussion in 'C++' started by Leslaw Bieniasz, Jun 7, 2006.

  1. 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: |
    *-------------------------------------------------------------------*
    | Interested in Computational Electrochemistry? |
    | Visit my web site: http://www.cyf-kr.edu.pl/~nbbienia |
    *-------------------------------------------------------------------*
     
    Leslaw Bieniasz, Jun 7, 2006
    #1
    1. Advertising

  2. Leslaw Bieniasz

    benben Guest

    Leslaw Bieniasz wrote:
    >
    > 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.

    >
    > L. B.


    Regards,
    Ben
     
    benben, Jun 7, 2006
    #2
    1. Advertising

  3. Leslaw Bieniasz

    benben Guest


    > 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
     
    benben, Jun 7, 2006
    #3
  4. Leslaw Bieniasz

    Daniel T. Guest

    In article <-kr.edu.pl>,
    Leslaw Bieniasz <> wrote:

    > 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"
     
    Daniel T., Jun 7, 2006
    #4
  5. Leslaw Bieniasz

    benben Guest

    Leslaw Bieniasz wrote:
    >
    > 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
     
    benben, Jun 7, 2006
    #5
  6. Leslaw Bieniasz

    Noah Roberts Guest

    Leslaw Bieniasz wrote:

    > 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.
     
    Noah Roberts, Jun 7, 2006
    #6
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Brenton Fletcher

    OOP Design Question

    Brenton Fletcher, Nov 19, 2003, in forum: Java
    Replies:
    4
    Views:
    511
    Brenton Fletcher
    Nov 20, 2003
  2. antoine
    Replies:
    7
    Views:
    480
    Rhino
    Mar 3, 2005
  3. KeithO

    dynamic_cast problem

    KeithO, Jul 2, 2004, in forum: C++
    Replies:
    3
    Views:
    570
    KeithO
    Jul 3, 2004
  4. Replies:
    2
    Views:
    376
    Jim Langston
    Jun 27, 2005
  5. Eric
    Replies:
    19
    Views:
    662
Loading...

Share This Page