Search code examples
c++classinheritancepolymorphismvirtual

C++ Parent class with virtual methods implemented in 2 different child classes


It was hard to make the title very clear about the subject, but I will try to explain the context (and there is some of the code down below). Note: I have seen similar questions answered, but they only treated the cases with 1 child class. So they didn't really help in my case, because I have 2 child classes.

Context: I have a parent class Shape that has 2 child: Circle and Square. I will have a vector of Shape objects, but these Shape objects will actually only be either Circle objects or Square objects. I need the Circle and Square classes to have the same parent class so that I can store them both in the same vector.

The trick is that I will need to use the Shape objects in the vector to call methods implemented in either the Circle class or the Square class, therefore, I need to have a "virtual" version of those methods in the parent class Shape.

Here is a simplified part of the code for my classes:

Shape.h :

class Shape{
public:
    std::string getColor();

    virtual int getRadius() = 0; //Method implemented in Circle
    virtual int getHeight() = 0; //Method implemented in Square
    virtual int getWidth() = 0;  //Method implemented in Square

protected:
    std::string color;
};

class Circle : public Shape{
public:
    int getRadius();

private:
    int radius;
};

class Square : public Shape{
public:
    int getHeight();
    int getWidth();

private:
    int height;
    int width;
};

In Shape.cpp I have something like this:

std::string Shape::getColor(){
    return color;
}

int Circle::getRadius(){
    return radius;
}

int Square::getHeight(){
    return height;
}

int Square::getWidth(){
    return width;
}

The errors occurs in the main.cpp when I want to create Circle and Square objects:

Circle *c = new Circle(...);//Error: cannot instantiate abstract class
                            //pure virtual function "Shape::getHeight" has no overrider
                            //pure virtual function "Shape::getWidth" has no overrider


Square *s = new Square(...);//Error: cannot instantiate abstract class
                            //pure virtual function "Shape::getRadius" has no overrider

So it seems like I would need a declaration of "getRadius" in the Square class and a declaration of "getHeight" and "getWidth" in the Circle class...

I tried adding them with virtual, but that makes Circle and Square abstract classes, so I can't create any objects with them.

Is there a way to make this work?

This is my first question posted on stackoverflow. I hope everything is clear. Thanks for the help!


Solution

  • Your virtual methods are not really good candidates for virtual methods because they have specific functionality for one class but no use for the other.

    Good example of virtual method would be something implemented by each class but with different functionality or result, like virtual int area() or virtual bool intersects( Shape * otherShape ) and so on.

    Anyway, this is how you would get your code compiled (with some extras):

    shape:

    class Shape{
    public:
        std::string getColor();
    
        Shape() {}
        virtual ~Shape() {}
    
        virtual int getRadius() { return 0; }  // no pure virtual
        virtual int getHeight() { return 0; }  // no pure virtual
        virtual int getWidth() { return 0; }   // no pure virtual
    
    protected:
        std::string color;
    };
    
    
    class Circle : public Shape {
    public:
        Circle( int r )
            : Shape()
            , radius( r )
        {}  
    
        Circle() : Circle( 0 ) {}
        ~Circle() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
    
        int getRadius() override { return radius; }; 
    
    private:
        int radius;
    };
    

    square:

    class Square : public Shape {
    public:
        Square( int h, int w )
            : Shape()
            , height( h )
            , width( w )
        {}  
    
        Square() : Square( 0, 0 ) {}
        ~Square() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
    
        int getHeight() override { return height; }
        int getWidth() override { return width; }
    
    private:
        int height;
        int width;
    };  
    

    test:

    int main() {
        using shapes = std::vector< Shape * >;
    
        shapes s;
        s.push_back( new Circle( 10 ) );
        s.push_back( new Square() );
        s.push_back( new Square( 1, 3 ) );
        s.push_back( new Circle() );
    
        for ( Shape * sh : s ) {
            std::cout
                << " r " << sh->getRadius()
                << " h " << sh->getHeight()
                << " w " << sh->getWidth()
                << std::endl;
        }       
    
        for ( Shape * sh : s ) { delete sh; } s.clear();
    }
    

    output:

    r 10 h 0 w 0
    r 0 h 0 w 0
    r 0 h 1 w 3
    r 0 h 0 w 0
    virtual Circle::~Circle()
    virtual Square::~Square()
    virtual Square::~Square()
    virtual Circle::~Circle()
    

    Here is a better use of virtual methods with area example:

    #include <iostream>
    #include <vector>
    
    struct Shape {
        Shape() {}
        virtual ~Shape() {}
    
        virtual double area() = 0;
    };
    

    extend with different area implementation:

    struct Circle : public Shape {
        Circle( int r )
            : Shape()
            , radius( r )
        {}
    
        Circle() : Circle( 0 ) {}
        ~Circle() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
    
        virtual double area() override { return radius * radius * 3.14; }
    
        int radius;
    };
    
    struct Square : public Shape {
        Square( int h, int w )
            : Shape()
            , height( h )
            , width( w )
        {}
    
        Square() : Square( 0, 0 ) {}
        ~Square() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
    
        virtual double area() override { return height * width; }
    
        int height;
        int width;
    };
    

    test

    int main() {
        using shapes = std::vector< Shape * >;
    
        shapes s;
        s.push_back( new Circle( 1 ) );
        s.push_back( new Square( 1, 1 ) );
        s.push_back( new Square( 2, 3 ) );
        s.push_back( new Circle( 2 ) );
    
        for ( Shape * sh : s ) {
            std::cout << sh->area() << std::endl;
        }
    
        for ( Shape * sh : s ) { delete sh; } s.clear();
    }
    

    output:

    3.14
    1
    6
    12.56
    virtual Circle::~Circle()
    virtual Square::~Square()
    virtual Square::~Square()
    virtual Circle::~Circle()