Thread: Virtual Base Classes

  1. #1
    Optics with a Twist skewray's Avatar
    Join Date
    Dec 2006
    Location
    South Pasadena, CA
    Posts
    36

    Virtual Base Classes

    I have a 20 year old, 100,000 line C program that I am trying to slowly convert to C++. Part of the code has a virtual base class with a large number of derived classes.

    Question 1: For each derived class, I want to have a function that takes a pointer to the base class and returns a boolean indicating if the pointer dereferences to that derived class. Right now I use a virtual function for every single derived class, which means that every time I add a new derived class, I have to edit every other derived class include file and put in a funtion that returns false. This can't be the right way to do this!

    Question 2: Say I have a function that takes a pointer particular derived class (bob) as an argument, or is a member function of class bob. Is there an elegant way to call this function on a pointer to the base class, assuming I have already checked that the pointer is really from the correct derived class? Right now I do a cast:
    Code:
    function( (bob*) pointer ) ;
    ( (bob*) pointer )->function() ;
    This is pretty ugly. There must be a better way!

    Thanks for any tips,

    Brian

  2. #2
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    You can use a dynamic_cast to test whether a base class pointer actually points to a specific derived class object (or one of its derived classes).
    Code:
    bool Derived::TestPointer(Base* ptr)
    {
        return dynamic_cast<Derived*>(ptr) != 0;
    }
    You would have to add just this one virtual function to all derived classes, but it would only be the one function, not separate functions with different names for each derived class type. Also note that this will return true if the pointer points to a class derived from Derived or its children.

    For question 2, if you have already checked that the pointer refers to the correct derived class, then a cast is the best way. I prefer a static_cast for this situation:
    Code:
    static_cast<bob*>(pointer)->function();
    Note that both of these situations are often (but not always) a signal that your design could be improved. You don't usually want to know what the base class pointer pointer points to, you just want to use its interface and let the derived classes implement the interface appropriately. You might want to consider rethinking whether the derived classes really model an is-a relationship.

  3. #3
    Optics with a Twist skewray's Avatar
    Join Date
    Dec 2006
    Location
    South Pasadena, CA
    Posts
    36
    So just to make sure I understand, I would use TestPointer like this:
    Code:
    if( bob::TestPointer( pointer ) ) ...
    As far as the casts, the relationships between the classes all make good sense. The problem is 100,000 lines of C, which originally used a tagged union of structs. I hope to get rid of most of the casts someday, but one thing at a time.

    Thanks!

    Brian

  4. #4
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    >> So just to make sure I understand, I would use TestPointer like this:
    Actually, that is correct. I made a mistake when I said it should be virtual, instead it should be static. You don't actually need a function, you can just use the code directly:
    Code:
    bob* derived_ptr = dynamic_cast<bob*>(pointer);
    if (derived_ptr)
    {
      derived_ptr->function(); // derived_ptr is already a bob* here, non need for static_cast.
    }

  5. #5
    Optics with a Twist skewray's Avatar
    Join Date
    Dec 2006
    Location
    South Pasadena, CA
    Posts
    36
    Wonderful! I read the section in my C++ book on dynamic_cast and static_cast five times and could make no sense of it. Now it makes sense.

    Brian

  6. #6
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    You can even do it this way:
    Code:
    if(bob *derived_ptr = dynamic_cast<bob*>(pointer)) {
      // use derived_ptr here
    }
    // derived_ptr is not visible here, so you can't accidentally use it.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  7. #7
    Optics with a Twist skewray's Avatar
    Join Date
    Dec 2006
    Location
    South Pasadena, CA
    Posts
    36
    The above bit of code now occurs about 200 times in my code. It looks *much* cleaner. Good suggestion!

  8. #8
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    >> The above bit of code now occurs about 200 times in my code.
    I know you said the relationships between the classes makes sense, but that just does not sound good. It will be a maintenance headache.

    Perhaps you can move the code that requires the dynamic_casting into member functions that are virtual and implemented only in the derived classes that have actual implementations.

  9. #9
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    I agree. That many virtual casts are indicative of a design problem. It may be a design problem inherited from the C version, but the way I see it, you should ask yourself this question: if now is not the time to change the design, when is? When the C++ version is done?
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  10. #10
    Optics with a Twist skewray's Avatar
    Join Date
    Dec 2006
    Location
    South Pasadena, CA
    Posts
    36
    I agree also. Most of my C++ coding problem are due to not knowing quite how to wrap my head around C++'s style of OOP. For example, the section I am working on now is a symbolic manipulation code, with about 10 different polymorphic types (numbers, operators, arrays, etc). The Compare() function ('==', in other words) currently accounts for ten of those dynamic casts, since it is a virtual function that looks like:
    Code:
    bool Derived::Compare( base* B ) {
        if( Derived* b = dynamic_cast<Derived*>(B) )
            return this.value() == b.value() ;                        // can compare same types
        else
            return false ;
        }
    I am certain that there is some cute way to write this function without a cast; I just don't know what it is yet. A template would cut the number down from 10 to 1, but that doesn't change the underlying code any.

  11. #11
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    Actually, that is a situation I came across a few years ago and asked about on a programming forum. In this case the dynamic_cast is an appropriate and valid solution, and it is how I solved my issue as well.

    If the only situations in which you are using the dynamic_cast are ones in which the functionality depends on the derived type of both operands, then you are probably ok.

  12. #12
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    It's called multiple dispatch and is a known issue in C++. One possible solution is the multiple dispatcher in the Loki library. See also "Modern C++ Design" by Aleksei Alexandrescu.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 39
    Last Post: 11-30-2007, 02:16 AM
  2. Destructors error
    By DarrenY in forum C++ Programming
    Replies: 7
    Last Post: 10-14-2007, 04:22 AM
  3. Replies: 3
    Last Post: 10-27-2006, 12:33 AM
  4. Virtual Data Base
    By aukh in forum C++ Programming
    Replies: 3
    Last Post: 04-04-2006, 05:58 AM
  5. Exporting Object Hierarchies from a DLL
    By andy668 in forum C++ Programming
    Replies: 0
    Last Post: 10-20-2001, 01:26 PM