473,326 Members | 2,111 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,326 software developers and data experts.

access public function of derived class through base class pointer

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[i]);
}
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?
Jul 22 '05 #1
9 4943

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[i]->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
Jul 22 '05 #2

"Banaticus Bart" <ba*******@yahoo.com> wrote in message
news:1a**************************@posting.google.c om...
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
Jul 22 '05 #3
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[i]);
}
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;
}
Jul 22 '05 #4
#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
Jul 22 '05 #5
(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)
Jul 22 '05 #6
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
Jul 22 '05 #7
> 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
Jul 22 '05 #8
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? :)
Jul 22 '05 #9
"Banaticus Bart" <ba*******@yahoo.com> wrote in message
news:1a**************************@posting.google.c om...
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
Jul 22 '05 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

2
by: Michael Young | last post by:
I've been trying to compile code similar to the example code below, but I keep getting errors indicating that the function 'foo()' is not accessible. At first, I thought this was a bug in the...
11
by: Roger Leigh | last post by:
The C++ book I have to hand (Liberty and Horvath, Teach yourself C++ for Linux in 21 Days--I know there are better) states that "static member functions cannot access any non-static member...
22
by: Nivvy | last post by:
HI all, I just wondered by seeing the o/p of the program. Let me know wt is the reason. class Base { public : virtual void f1() {
1
by: Dave | last post by:
Hello NG, Regarding access-declarations and member using-declarations as used to change the access level of an inherited base member... Two things need to be considered when determining an...
3
by: Jeremy Jurksztowicz | last post by:
Aside from the obvious runtime failure this would cause, this should compile correctly... right? I racked my brain over this because it seems so obvious, but my compiler (GCC 4.0... I think?) is...
3
by: Samuel Burri | last post by:
Hi there I got a simple problem. As you can see in the posted source, I have two classes, where one is derived from the other. In fact, they also contain virtual functions. In the function...
13
by: dragoncoder | last post by:
Consider the following code #include <iostream> class Base { public: virtual void say() { std::cout << "Base" << std::endl; } }; class Derived: public base {
15
by: =?Utf-8?B?R2Vvcmdl?= | last post by:
Hello everyone, I met with a strange issue that derived class function can not access base class's protected member. Do you know why? Here is the error message and code. error C2248:...
10
by: blangela | last post by:
If I pass a base class object by reference (likely does not make a difference here that it is passed by reference) as a parameter to a derived class member function, the member function is not...
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, you’ll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
1
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: jfyes | last post by:
As a hardware engineer, after seeing that CEIWEI recently released a new tool for Modbus RTU Over TCP/UDP filtering and monitoring, I actively went to its official website to take a look. It turned...
1
by: PapaRatzi | last post by:
Hello, I am teaching myself MS Access forms design and Visual Basic. I've created a table to capture a list of Top 30 singles and forms to capture new entries. The final step is a form (unbound)...
1
by: Defcon1945 | last post by:
I'm trying to learn Python using Pycharm but import shutil doesn't work
1
by: Shællîpôpï 09 | last post by:
If u are using a keypad phone, how do u turn on JavaScript, to access features like WhatsApp, Facebook, Instagram....
0
by: af34tf | last post by:
Hi Guys, I have a domain whose name is BytesLimited.com, and I want to sell it. Does anyone know about platforms that allow me to list my domain in auction for free. Thank you
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.