access public function of derived class through base class pointer

Discussion in 'C++' started by Banaticus Bart, May 3, 2004.

  1. I wrote an abstract base class from which I've derived a few other
    classes. I'd like to create a base class array where each element is
    an instance of a derived object. I can create a base class pointer
    which points to an instance of a derived class, but when I pass that
    base class pointer into a function, it can't access the derived
    object's public functions. Although, the base class pointer does call
    the appropriate virtual function in the derived class.

    Here's a section of code, I've posted comments in my code to more
    clearly communicate the problem I'm having.

    int main()
    {
    void printTestObject(BAHshape &);
    BAHshape testArray[] = {BAHcircle(2.5), BAHrectangle(3.1, 2),
    BAHcube(4, 2, 3)};

    for (int i = 0; i < 3; i++)
    {
    printTestObject(testArray);
    }
    system("pause");
    system("cls");
    } //end main function

    void printTestObject(BAHshape &testObject)
    {
    /*
    The following three lines display correctly. When I pass in
    testArray[0], the program displays "The area of this circle is ..."
    the correct calcArea() function is called in each instance -- the
    circle object calculates the area from the circle class calcArea()
    function, the rectangle calculates the area from the rectangle class
    calcArea() function, etc.
    */

    std::string objectName = testObject.getName();
    std::cout <<"The area of this " <<testObject.getName() <<" is: ";
    std::cout <<testObject.getArea() <<std::endl;

    /*
    The following sction of code (which is commented out) is what I'm
    having a problem with. Seeing as how I'm passing in a BAHshape object
    to this function, the compiler won't allow the function getRadius().
    How can I allow a BAHshape pointer (which points to an instance of a
    derived class) to access the public functions in BAHcircle?
    */
    /*
    if (objectName == "circle")
    {
    std::cout <<"This circle's radius is: ";
    std::cout <<testObject.getRadius();
    }

    //this second if block could have been an else if, but I felt that
    //a seperate if block made for better readability.
    if (objectName == "rectangle" || objectName == "cube")
    {
    std::cout <<"The ";
    std::cout <<testObject.getName();
    std::cout <<"'s base is ";
    std::cout <<testObject.getBase();
    std::cout <<" and its height is ";
    std::cout <<testObject.getHeight();
    if (objectName == "cube")
    {
    std::cout <<" and its depth is ";
    std::cout <<testObject.getDepth();
    }
    std::cout <<".\n";
    }
    */

    std::cout <<"The " <<testObject.getName() <<" is located at (";
    std::cout <<testObject.getXCoordinate() <<", ";
    std::cout <<testObject.getYCoordinate() <<", ";
    std::cout <<testObject.getZCoordinate() <<").\n";
    std::cout <<std::endl;
    } //end printTestObject function

    This is all rather frustrating. Because I can't instantiate a
    BAHshape object (since I have a pure virtual function in that class),
    I can't do the following:
    BAHshape testArray[2];
    testArray[0] = BAHcircle(2.5);
    testArray[1] = BAHrectangle(3.1, 2);
    The compiler won't allow me to put any constant integer in the
    brackets after the name testArray. It's somewhat weird that I can't
    create a base class object, but I can create a base class pointer.

    So, any hints on how I can allow the base class pointer (which points
    to an instance of a derived class) to access the public functions in
    that derived class?
    Banaticus Bart, May 3, 2004
    #1
    1. Advertising

  2. Banaticus  Bart

    Howard Guest

    I think the best approach would be to have the printTestObject itself be a
    virtual (and abstract) member of the base class, and override it for each
    sub-class. Then, for the circle object's overridden version, it would
    already know it's a circle, and could say so and then get and print the
    radius with no problem. Likewise, the rectangle's overriden version would
    output the rectangle info. That's a much better way to use derived objects
    than testing for the object type and calling functions based on the type.

    As for what you store in the array, you *should* be storing pointers,
    because, as you noticed, you can store base class pointers with no problem.
    And then, when you call, for example, testArray->printTestObject(),
    you'll get the correct overriden version for the specific instance you've
    created. This means you have to manage the creation and destruction of the
    objects via those pointers instead of using an initializer list, but it is
    how to do this properly.

    -Howard
    Howard, May 3, 2004
    #2
    1. Advertising

  3. "Banaticus Bart" <> wrote in message
    news:...
    > I wrote an abstract base class from which I've derived a few other
    > classes. I'd like to create a base class array where each element is
    > an instance of a derived object. I can create a base class pointer
    > which points to an instance of a derived class, but when I pass that
    > base class pointer into a function, it can't access the derived
    > object's public functions. Although, the base class pointer does call
    > the appropriate virtual function in the derived class.
    >
    > Here's a section of code, I've posted comments in my code to more
    > clearly communicate the problem I'm having.
    >


    I think you need to read about polymorphism in a good C++ book. Your code is
    so full of errors I don't know where to start. In fact I'm somewhat
    surprised you got any reasonable behaviour out of it at all. I don't think
    it should even compile.

    Here's a couple of pointers.

    1) Polymorphism is all about pointers. You do not create an array of base
    class objects, you create an array of pointers to base class objects. You
    cannot get polymorphic behaviour from objects, it must be pointers (or
    references).

    2) The answer to your actual question is you don't need to. In the code you
    posted you should create a virtual function called print, which prints out
    the details of each object. Then you don't need to access the public methods
    of derived classes. If you really do need to access the public methods of a
    derived class, then the only way is to cast the base class pointer to a
    derived class pointer using static_cast or dynamic_cast.

    Hope this helps,
    john
    John Harrison, May 3, 2004
    #3
  4. Thanks for your help -- I've changed the tester file to use an array
    of pointers, but for some reason the variable name is getting set to a
    space for every object. The constructor is being called for each
    object, since calcArea is only called in the derived constructors, and
    the correct area is being printed, but the constructor doesn't seem to
    be setting the variable name correctly. getName() returns " " -- see
    the comments in the printTestObject function. I'll post the code for
    the basic shape and the (derived) circle classes as a reply to this
    post.

    #include <iostream>
    #include "BAHshape.h"
    #include "BAHcircle.h"
    #include "BAHrectangle.h"
    #include "BAHcube.h"

    int main()
    {
    void printTestObject(BAHshape *);

    const int testArraySize = 3;
    BAHshape *testArray[testArraySize]={&BAHcircle(2.5, 1, 2),
    &BAHrectangle(3.1, 2, 2, 0, 5),
    &BAHcube(4, 2, 3, 2)};
    for (int i = 0; i < testArraySize; i++)
    {
    printTestObject(testArray);
    }
    system("pause");
    system("cls");
    } //end main function

    void printTestObject(BAHshape *testObject)
    {
    //getName returns " " for some reason
    std::string objectName = testObject->getName();

    //the next two lines display: "The area of this is: [area]"
    //note that objectName is a space for some reason
    std::cout <<"The area of this " <<testObject->getName() <<" is: ";
    std::cout <<testObject->getArea() <<std::endl;

    //since objectName is " ", the following loop is skipped
    if (objectName == "circle")
    {
    BAHcircle *tempObject = dynamic_cast<BAHcircle*>(testObject);
    std::cout <<"This circle's radius is: ";
    std::cout <<tempObject->getRadius();
    }

    //this second if block could have been an else if, but I felt that
    //a seperate if block made for better readability.
    if (objectName == "rectangle" || objectName == "cube")
    {
    BAHrectangle *tempObject = dynamic_cast<BAHrectangle*>(testObject);
    std::cout <<"The ";
    std::cout <<tempObject->getName();
    std::cout <<"'s base is ";
    std::cout <<tempObject->getBase();
    std::cout <<" and its height is ";
    std::cout <<tempObject->getHeight();
    if (objectName == "cube")
    {
    BAHcube *tempObject = dynamic_cast<BAHcube*>(testObject);
    std::cout <<" and its depth is ";
    std::cout <<tempObject->getDepth();
    }
    std::cout <<".\n";
    }

    std::cout <<"The " <<testObject->getName() <<" is located at (";
    std::cout <<testObject->getXCoordinate() <<", ";
    std::cout <<testObject->getYCoordinate() <<", ";
    std::cout <<testObject->getZCoordinate() <<").\n";
    std::cout <<std::endl;
    }
    Banaticus Bart, May 4, 2004
    #4
  5. Re: the basic shape class

    #ifndef BAHSHAPE_H
    #define BAHSHAPE_H

    #include <string>
    class BAHshape
    {
    protected:
    double area; //used to hold area of shapes
    double base; //used to hold base of shapes
    std::string name;
    long xCoordinate;
    long yCoordinate;
    long zCoordinate;

    virtual void calcArea() = 0;
    void setXCoordinate(const long x) {xCoordinate = x;}
    void setYCoordinate(const long y) {yCoordinate = y;}
    void setZCoordinate(const long z) {zCoordinate = z;}
    public:
    /* There's no need for a constructor -- since I have a pure virtual
    function, this class has become a pure abstract base class, and cannot
    be instantiated.
    */

    /* moveBack() and moveForward() are used to move shapes forward and
    back on the z-axis so as to partially hide and partially cover other
    shapes in the drawing program
    */
    void moveBack() {setZCoordinate(getZCoordinate() - 1);}
    void moveCenter() {setZCoordinate(0);}
    void moveForward() {setZCoordinate(getZCoordinate() + 1);}

    const double getArea() {return area;}
    const std::string getName() {return name;}
    const long getXCoordinate() {return xCoordinate;}
    const long getYCoordinate() {return yCoordinate;}
    const long getZCoordinate() {return zCoordinate;}

    //uses setCoordinate functions to set coordinate positions
    bool setPosition(const long int, const long int, const long int);
    };
    #endif
    Banaticus Bart, May 4, 2004
    #5
  6. Re: derived circle class

    (forgot to add text from BAHshape.cpp)
    #include "BAHshape.h"
    bool BAHshape::setPosition(const long x, const long y, const long z)
    {
    setXCoordinate(x);
    setYCoordinate(y);
    setZCoordinate(z);
    return true;
    }
    (that's all of it)

    #ifndef BAHCIRCLE_H
    #define BAHCIRCLE_H

    #include "BAHshape.h"

    class BAHcircle : public BAHshape
    {
    protected:
    //The "base" variable supplied by BAHshape is implemented in this class
    //rather than the normal "radius" variable.
    //In this class, the term "base" is used in place of the normal term "diameter"

    /*
    * Following is the derivation for the calcArea formula
    * {area = 314159 * radius * radius;}
    * {area = 314159 * (.5 * base) * (.5 * base);} //replace (radius) with (.5 * base)
    * {area = 314159 * .5 * .5 * base * base;} //(A*B)*(A*B) = A*A*B*B
    * {area = 3.14159 * .25 * base * base;} //simply somewhat
    * {area = .7853975 * base * base;}//simplify further
    */
    virtual void calcArea() {area = .7853975 * base * base;}
    public:
    //double is radius -- longs are coordinate locations
    BAHcircle(double = 0, long = 0, long = 0, long = 0);

    const double getDiameter() {return base;}
    const double getRadius() {return (base * .5);}
    bool setDiameter(const double d) {return (setRadius(d / 2));}
    //on .cpp page -- requires error checking
    bool setRadius(const double);
    };
    #endif
    (that's the end of BAHcircle.h)

    (Begin BAHcircle.cpp)
    #include "BAHcircle.h"

    BAHcircle::BAHcircle(double r, long x, long y, long z)
    {
    if (r < 0)
    base = 0;
    else
    base = 2 * r;
    calcArea();
    setPosition(x, y, z);
    name = "circle";
    }

    bool BAHcircle::setRadius(const double r)
    {
    //first check to make sure value is not less then 0
    if (r < 0) return false;
    //since value is 0 or more, save it and calculate relevent information
    base = 2 * r;
    BAHcircle::calcArea();
    return true;
    }
    (End BAHcircle.cpp)
    Banaticus Bart, May 4, 2004
    #6
  7. Re: more on problem with getName()

    Thanks for all your help. :)

    I've tried making variable name a constant variable -- so that once
    set to the name of a class it can't be changed. But, the cube class
    constructor calls the rectangle class constructor. So, name is set in
    the rectangle class constructor, and then the cube class can't change
    variable name. I'm at a loss for why name would be set to " ".

    I heard it said that my code was so full of errors that a person was
    surprised that it would even run. Although I appreciate the help that
    this person offered in reminding me of dynamic_cast, I would also
    appreciate it if the other errors in my code could be pointed out to
    me so that I could fix them as well. :)

    Thanks again,
    Bart
    Banaticus Bart, May 4, 2004
    #7
  8. > const int testArraySize = 3;
    > BAHshape *testArray[testArraySize]={&BAHcircle(2.5, 1, 2),
    > &BAHrectangle(3.1, 2, 2, 0, 5),
    > &BAHcube(4, 2, 3, 2)};


    This is not valid, BAHcircle(2.5, 1, 2) and the rest are temporary objects.
    They are destroyed before you get to the next line, so you are left with
    pointers in your array which are pointing to already destroyed objects.
    Everything after that is going to fail.

    It's completely different from this

    const int testArraySize = 3;
    BAHshape testArray[testArraySize]={BAHcircle(2.5, 1, 2),
    BAHrectangle(3.1, 2, 2, 0, 5),
    BAHcube(4, 2, 3, 2)};

    which is valid because the temporary objects are copied into your array
    before they are destroyed.

    Have you heard of dynamic memory allocation? If not then I suggest you
    forget about polymorphism for a while and start learning about that.

    Using dynamic memory allocation I would do something like this

    const int testArraySize = 3;
    BAHshape* testArray[testArraySize];
    testArray[0] = new BAHcircle(2.5, 1, 2);
    testArray[1] = new BAHrectangle(3.1, 2, 2, 0, 5);
    testArray[2] = new BAHcube(4, 2, 3, 2);

    john
    John Harrison, May 4, 2004
    #8
  9. OK, I rewrote my shape classes. Previously I had:

    shape class //variable "base" is in this class -- all objects which
    inherit this class have access to protected function setBase(), but
    not to private variable "base"
    ->circle class // normally we'd call "base" "diameter"
    ->rectangle class //adds height variable
    (from rectangle) ->cube class // adds depth variable

    But, then I ran into a problem. I wanted to write a triangle class.
    It would inherit from the basic shape class, but it needed a height
    variable to properly compute the area. The rectangle class has a
    height variable, but I hesitated to put class triangle and class cube
    on the same playing field (to have class triangle inherit from class
    rectangle just like class cube) -- it just seemed almost intuitively
    wrong. I also didn't want to rewrite class Rectangle and simply give
    it a different name -- triangle.

    So, I created a 2D class which inherited the shape class and which had
    a private height variable with a protected setHeight() function. I
    then had class triangle inherit this class. I could also (with slight
    modification) have class rectangle and class circle inherit this class
    (the height and base are always set to be the same in class circle).

    Following that pattern, I decided to create a 3D class which could be
    inherited by class cube and class cyliner, etc. But, I wanted to
    follow the previous pattern I had in which class cylinder's
    constructor called class rectangle's constructor. But, the only way
    to do this was to inherit both class 2D and class rectangle. Here's
    where I started to run into problems.

    For, now, class cube could trace its way back to class 2D in two ways
    -- through class 2D and through class rectangle. No problem, I
    thought, I'd just follow the ElectronicDevice class format and declare
    class 2D to be virtual in accordance to the following:
    class ElectronicDevice
    class Fax : virtual public ElectronicDevice
    class Modem : virtual public ElectronicDevice
    class FaxModem : public Fax, public modem
    I.e., class 3D and class rectangle would inherit class 2D virtually.
    But, that created another problem.

    In my test file, I had (as you all remember), something akin to the
    following: shape *testArray[3] = {new circle(3), new rectangle(3, 4),
    new cube(2, 4, 3)};
    And, then when I wanted to access the public functions in class
    circle, etc., I static cast the pointer to the proper class:
    cout << static_cast<Rectangle*>(testArray[1])->getHeight();

    To recap, I had something akin to the following inheritance pattern:
    class shape
    class 2D : public shape
    class Circle : public 2D
    class Rectangle : virtual public 2D
    class Triangle : public 2D
    class 3D : virtual public 2D
    class Cube : public 3D, public Rectangle

    But, since 2D is now (due to the virtual descriptors in class
    Rectangle and class 3D) considered to be a virtual class. I can do
    the following:
    cout << static_cast<2D*>(testArray[1])->getHeight();
    but I cannot do the following:
    cout << static_cast<3D*>(testArray[1])->getDepth();

    In other words, I can cast the basic shape pointer into a 2D pointer,
    but I cannot cast through the 2D class. Perhaps I'm simply going
    about this problem the wrong way. Perhaps my problem could be
    restated thusly: Currently class Cube inherits class 3D class
    Rectangle. What I really want is for class Cube to have access to
    class Rectangle's constructor (which, I believe requires inheritance),
    and to have access to class 3D's variables.

    Hmm, perhaps this specific problem can be solved rather easily --
    perhaps if I fully qualify class 3D's functions that I use in class
    Cube, and only inherit class Rectangle I'll be fine. But, what about
    more general cases? What if I really do have a FaxModem class, and I
    need access to the constructors in both the Fax class and the Modem
    class? And, so that I don't have to write fifty'leven ;) functions, I
    want to be able to check for what type of class it is, and (based on
    the result of that check) cast the basic pointer to a derived class?

    Should I restate any of that? Any suggestions? :)
    Banaticus Bart, May 7, 2004
    #9
  10. "Banaticus Bart" <> wrote in message
    news:...
    > OK, I rewrote my shape classes. Previously I had:
    >
    > shape class //variable "base" is in this class -- all objects which
    > inherit this class have access to protected function setBase(), but
    > not to private variable "base"
    > ->circle class // normally we'd call "base" "diameter"
    > ->rectangle class //adds height variable
    > (from rectangle) ->cube class // adds depth variable
    >
    > But, then I ran into a problem. I wanted to write a triangle class.
    > It would inherit from the basic shape class, but it needed a height
    > variable to properly compute the area. The rectangle class has a
    > height variable, but I hesitated to put class triangle and class cube
    > on the same playing field (to have class triangle inherit from class
    > rectangle just like class cube) -- it just seemed almost intuitively
    > wrong. I also didn't want to rewrite class Rectangle and simply give
    > it a different name -- triangle.
    >
    > So, I created a 2D class which inherited the shape class and which had
    > a private height variable with a protected setHeight() function. I
    > then had class triangle inherit this class. I could also (with slight
    > modification) have class rectangle and class circle inherit this class
    > (the height and base are always set to be the same in class circle).
    >
    > Following that pattern, I decided to create a 3D class which could be
    > inherited by class cube and class cyliner, etc. But, I wanted to
    > follow the previous pattern I had in which class cylinder's
    > constructor called class rectangle's constructor. But, the only way
    > to do this was to inherit both class 2D and class rectangle. Here's
    > where I started to run into problems.
    >
    > For, now, class cube could trace its way back to class 2D in two ways
    > -- through class 2D and through class rectangle. No problem, I
    > thought, I'd just follow the ElectronicDevice class format and declare
    > class 2D to be virtual in accordance to the following:
    > class ElectronicDevice
    > class Fax : virtual public ElectronicDevice
    > class Modem : virtual public ElectronicDevice
    > class FaxModem : public Fax, public modem
    > I.e., class 3D and class rectangle would inherit class 2D virtually.
    > But, that created another problem.
    >
    > In my test file, I had (as you all remember), something akin to the
    > following: shape *testArray[3] = {new circle(3), new rectangle(3, 4),
    > new cube(2, 4, 3)};
    > And, then when I wanted to access the public functions in class
    > circle, etc., I static cast the pointer to the proper class:
    > cout << static_cast<Rectangle*>(testArray[1])->getHeight();
    >
    > To recap, I had something akin to the following inheritance pattern:
    > class shape
    > class 2D : public shape
    > class Circle : public 2D
    > class Rectangle : virtual public 2D
    > class Triangle : public 2D
    > class 3D : virtual public 2D
    > class Cube : public 3D, public Rectangle
    >
    > But, since 2D is now (due to the virtual descriptors in class
    > Rectangle and class 3D) considered to be a virtual class. I can do
    > the following:
    > cout << static_cast<2D*>(testArray[1])->getHeight();
    > but I cannot do the following:
    > cout << static_cast<3D*>(testArray[1])->getDepth();
    >
    > In other words, I can cast the basic shape pointer into a 2D pointer,
    > but I cannot cast through the 2D class. Perhaps I'm simply going
    > about this problem the wrong way. Perhaps my problem could be
    > restated thusly: Currently class Cube inherits class 3D class
    > Rectangle. What I really want is for class Cube to have access to
    > class Rectangle's constructor (which, I believe requires inheritance),
    > and to have access to class 3D's variables.
    >
    > Hmm, perhaps this specific problem can be solved rather easily --
    > perhaps if I fully qualify class 3D's functions that I use in class
    > Cube, and only inherit class Rectangle I'll be fine. But, what about
    > more general cases? What if I really do have a FaxModem class, and I
    > need access to the constructors in both the Fax class and the Modem
    > class? And, so that I don't have to write fifty'leven ;) functions, I
    > want to be able to check for what type of class it is, and (based on
    > the result of that check) cast the basic pointer to a derived class?
    >
    > Should I restate any of that? Any suggestions? :)


    I read most of this, but frankly think you are on a wrong track. Your idea
    of using rectangle and 3D as base classes for a cube strike me as odd.
    I would think of it like this: A rectangle has all the necessary members to
    handle a 2D figure with two pairs of parallel sides. A cube is a
    specialization of a rectangle that has height and volume in addition to
    everything else a rectangle has. So a cube class would inherit from
    rectangle. And that's all.
    The constructor for a cube can use the constructor of the rectangle plus its
    additional code to initialize height. The class shape is a virtual class
    that each specialized shape derives from, presumably having pure virtual
    functions for those operations you want to enforce on any derived classes,
    such as calculateArea, or setBackgroundColor, or such.
    See if this simplification doesn't cover all that you want to do.
    --
    Gary
    Gary Labowitz, May 7, 2004
    #10
    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. tirath
    Replies:
    3
    Views:
    694
    Ivan Vecerina
    Oct 12, 2003
  2. Zycor
    Replies:
    3
    Views:
    5,090
    David Harmon
    Dec 17, 2004
  3. Replies:
    1
    Views:
    382
    myork
    May 23, 2007
  4. Replies:
    1
    Views:
    371
    Victor Bazarov
    May 23, 2007
  5. , India
    Replies:
    8
    Views:
    930
    gwowen
    Aug 18, 2010
Loading...

Share This Page