Max
Max

Reputation: 416

What is the right solution to access subclass variables?

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

Answers (4)

anastaciu
anastaciu

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:

Here is how I would do it:

#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

Adrian Mole
Adrian Mole

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?.


EDIT: In your specific case, the answer given by rustyx is really the 'correct' approach; however, it is useful to understand/appreciate the use of the 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

463035818_is_not_an_ai
463035818_is_not_an_ai

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

rustyx
rustyx

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

Related Questions