Reputation: 416
How can I access sideA
and height
members of Triangle
class, and how can I access sideA
of a Square
class, these are both derived from Shape
class?
What is the correct way to implement that?
Shapes.h:
class Shape
{
public:
virtual double getArea() = 0;
};
class Triangle : public Shape
{
public:
double sideA = 3;
double height = 2;
double getArea() {
return 0.5 * sideA * height;
}
};
class Square : public Shape
{
public:
double sideA = 4;
double getArea() {
return sideA * sideA;
}
};
Main.cpp:
int main()
{
Shape* sh = new Triangle();
std::cout << sh->getArea() << std::endl;
std::cout << sh->??? //get the height of triangle
delete sh;
}
Upvotes: 2
Views: 76
Reputation: 23802
You can declare the variable a Triangle*
not a Shape*
, this way you'll have access to the derived class and base class methods and variables:
int main()
{
Triangle* sh = new Triangle();
Square* sh2 = new Square();
std::cout << sh->getArea() << std::endl; //3
std::cout << sh2->getArea() << std::endl; //16
std::cout << sh->sideA << std::endl; //3
std::cout << sh2->sideA << std::endl; //4
delete sh;
}
To use delete sh
safely you should have a virtual destructor
class Shape
{
public:
virtual double getArea() = 0;
virtual ~Shape(){} //virtual destructor
};
Since you already have an abstract class, why not use it to access the the data in the derived classes:
#include <iostream>
#include <memory>
class Shape
{
private:
double sideA; //shared members can be defined in base class, assuming all
//derived classes will have sideA member
protected:
Shape(double sideA) : sideA(sideA) {}//for initialization of sideA in derived classes
public:
Shape() = default;
virtual double getArea() = 0;
double getSideA() { //shared logic
return sideA;
}
virtual ~Shape(){} //virtual destructor
};
class Triangle : public Shape
{
private:
double height = 2; //specific property
public:
Triangle() : Shape(3) {} //intialize sizeA
double getHeight(){ //specific method, must instanciate Triangle to access
//for collections it's best to use interface method like getArea()
return height;
}
double getArea() override {
return 0.5 * getSideA() * height;
}
};
class Square : public Shape
{
public:
Square() : Shape(4) {} //intialize sizeA
double getArea() override {
return getSideA() * getSideA();
}
};
int main()
{
std::unique_ptr<Shape> sh(new Triangle); //smart pointer
std::unique_ptr<Shape> sh2(new Square); //smart pointer
std::cout << sh->getArea() << std::endl; //3
std::cout << sh2->getArea() << std::endl; //16
std::cout << sh->getSideA() << std::endl; //3
std::cout << sh2->getSideA() << std::endl; //4
//delete sh; //no need, smart pointer
}
Take a look at smart pointers.
Upvotes: 2
Reputation: 51825
Because your base class has a virtual
function1, you can use the dynamic_cast
conversion to check if a pointer to it is actually a pointer to one of its derived classes. This will return nullptr
if it is not of the 'tested' class, or a usable pointer to the derived class, if it is:
int main()
{
Shape* sh = new Triangle();
std::cout << sh->getArea() << std::endl;
if (dynamic_cast<Square*>(sh) != nullptr) { // Check for a valid Square pointer
Square* sq = dynamic_cast<Square*>(sh);
std::cout << sq->sideA << std::endl;
}
else if (dynamic_cast<Triangle*>(sh) != nullptr) { // Check for a valid Trianlge pointer
Triangle* tr = dynamic_cast<Triangle*>(sh);
std::cout << tr->height << std::endl;
}
else {
std::cout << "Unspecified shape type: height unknown!" << std::endl;
}
delete sh;
return 0;
1 Note that, because you have a virtual function in your Shape
class, you should also give it a virtual destructor:
class Shape {
public:
virtual double getArea() = 0;
virtual ~Shape() { }
};
For further discussion on the need for a virtual destructor, see here: When to use virtual destructors?.
dynamic_cast
option, as this can be the only solution if you are deriving classes from a third-party base class, which you cannot modify, and thus cannot add the equivalent of the getHeight()
function to it.
Upvotes: 2
Reputation: 122458
A Shape
has no height
. You are using the triangle polymorphically. That means you have a Shape*
and can only use the interface of Shape
, no matter what is the actual type of the object. If you want a Triangle
then use a Triangle
not a Shape
. If you still want to use a Triangle
and Rectangle
polymorphically, then you should put the common interface into the base class. In your case, both have a sideA
, so you could do:
struct Shape {
double sideA = 3;
virtual double getArea() = 0;
virtual ~Shape(){}
};
struct Triangle : public Shape {
double height = 2;
double getArea() {
return 0.5 * sideA * height;
}
};
struct Square : public Shape {
double getArea() {
return sideA * sideA;
}
};
int main() {
Shape* sh = new Triangle();
std::cout << sh->sideA;
delete sh;
}
PS: the above wasn't the whole truth. If you have a Shape*
and you know that it is a Triangle*
then you could use dynamic_cast
, but doings such casts are often a sign for poor design. You should strive to write classes such that you do not need a cast.
Upvotes: 2
Reputation: 85341
You are trying to access information that is not available via the interface you defined, class Shape
allows only the area to be accessed.
To get also the height, the proper way is to extend the interface to provide that information as well.
class Shape
{
public:
virtual double getArea() = 0;
virtual double getHeight() = 0;
};
class Triangle : public Shape
{
public:
double sideA = 3;
double height = 2;
double getArea() {
return 0.5 * sideA * height;
}
double getHeight() {
return height;
}
};
class Square : public Shape
{
public:
double sideA = 4;
double getArea() {
return sideA * sideA;
}
double getHeight() {
return sideA;
}
};
Upvotes: 3