0

Suppose I want to define a class named Circle, which has methods to calculate its area and perimeter. This class uses another class named Point. Suppose also that this class inherits from an abstract class named Shape.

So, I wrote the following code:

circle.h

#ifndef Circle_h
#define Circle_h
#include "Point.h"
#include "Shape.h"

class Circle : public Shape
{
public:

    Circle(const Point &ceneter , int radius);
    ~Circle();

    virtual double getArea() const ;
    virtual double getPerim() const ;

private:
    int radius;
    Point center;
};

#endif 

shape.h

#ifndef Shape_h
#define Shape_h
#include "Point.h"
#include <iostream>

class Shape          //abstract class//
{
public:

    virtual double getArea() const=0;
    virtual double getPerim() const=0;
};

#endif 

point.h

#ifndef Point_h
#define Point_h

class Point
{
public:

    Point(int x, int y);
    ~Point();

    int getX() const;
    int getY() const;

    void setX(int x);
    void setY(int y);

private:

    int x, y;
};

#endif 

I am not so sure about 2 things:

  1. Should I add a c'tor and d'ctor to the Shape class?

    Cause as far as I understand it, since that class is abstract, there is no need in them.

  2. Should I add override to the overridden methods in Circle, like this?

    virtual double getArea() const override ;
    virtual double getPerim() const override ;
    
  3. Suppose I write the following (in the main):

    Point o(0, 0);
    
    Point a(0, 1);
    
    Point b(1, 0);
    
    Shape *shapes[] = { new Circle(a, 2), new Circle(b,3), new Circle(o, 1 };
    

How does the last line whould influence on the need to use (or not use) c'tor and d'tor?

TRUMPI
  • 33
  • 8

3 Answers3

1

Your Shape class doesn’t need a ctor but it is advisible to give it a virtual dtor:

class Shape {
public:
    virtual ~Shape() = default;
    // ...
};

Without this virtual destructor it is undefined behavior to delete an object via a pointer to Shape: no concrete type can be of dynamic type Shape but deleteing an object through a pointer with a different static type is undefined behavior when the dtor isn’t virtual.

With respect to the use of override I recommend using it! While it probably doesn’t matter in a simple example like the one you showed it will become relevant when the class hierarchy grows. Any successful software will grow and it will add virtual functions and code clarity helps maintenance. ... and I prefer to plan for success.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
0

As written none of these classes need either a copy constructor or a destructor. This is because all of the members are simple values that do not require anything more then a value copy or for the memory to simply be released.

A dtor is needed when the class has resources (such as a file handle) that the compiler cannot know how to free.

SoronelHaetir
  • 14,104
  • 1
  • 12
  • 23
  • 2
    A `virtual` destructor goes beyond that though. It's required for the base type if you are going to destroy derived objects using pointers to that base type. – François Andrieux Dec 20 '17 at 20:01
0

Should I add override to the overridden methods in Circle, like this?

IMHO, the term adds value.


Suppose I write the following (in the main):

Point o(0, 0); 
Point a(0, 1); 
Point b(1, 0); 
Shape* shapes[] = { new Circle(a, b), 
                    new Circle(o, a, b), 
                    new Circle(o, 1) };

With these lines, my compiler now reports 2 additional errors (with 2 notes) ...

error: no matching function for call to ‘Circle::Circle(Point&, Point&)’
note:   no known conversion for argument 2 from ‘Point’ to ‘int’

also

error: no matching function for call to ‘Circle::Circle(Point&,Point&,Point&)’
note:   candidate expects 2 arguments, 3 provided

Does the last line influence the need to use (or not use) c'tor and d'tor?

I think not.

Even before this 'code addition' the compiler gives warnings about having virtual functions and accessible non-virtual d'tors. These warnings might be fixed by declaring the (missing) dtors virtual, but ... well, here they simply point out that your code does not compile clean.


You should complete your [MCVE]. As is, you provide insufficient information for a 'careful pick' of what (ctors, dtors) you do or do not provide.

I usually come at these issues in another way. My personal coding 'standard' includes the following 6 ideas that, when I am in doubt, or don't understand why, I simply add these lines to my classes, in a private section.

  //  coding standard: disallow when not used
  T(void)                  = delete; // default ctor    (1)
 ~T(void)                  = delete; // default dtor    (2)
  T(const T&)              = delete; // copy ctor       (3)
  T(const T&&)             = delete; // move ctor       (4)
  T& operator= (const T&)  = delete; // copy assignment (5)
  T& operator= (const T&&) = delete; // move assignment (6)

By 'T', I mean for you to replace the 'T' with your class name. It is easy, so I usually add to each of my classes. i.e. repeat the above 6 lines, in a private area of each class, with T replaced.

Now, your compiler can complain about something specific. I have found it quick to learn how to fix the issues this shows, so I offer one example.

If you ever get a MCVE, try the following additions:

class Shape          //abstract class//
{
public:
    virtual double getArea() const=0;
    virtual double getPerim() const=0;

private:
   // coding standard - disallow when not used
   Shape(void)                      = delete; // default ctor    (1)
  ~Shape(void)                      = delete; // default dtor    (2)
   Shape(const Shape&)              = delete; // copy ctor       (3)
   Shape(const Shape&&)             = delete; // move ctor       (4)
   Shape& operator= (const Shape&)  = delete; // copy assignment (5)
   Shape& operator= (const Shape&&) = delete; // move assignment (6)
};

Since the Shape class has only the compiler injected default ctor, (and dtor) I think these deletes will trigger an error in the compiler.

The idea is simply that if 'other code' (such as standard containers, or algorithms, or the compiler) does things with Shape and uses one or adds one or more of these 6, the compiler will notice that they are deleted, and generate an error for you.

I have been surprised by the standard containers as they often use more of these 6 than I expect. With the ctor deleted, I get a chance to decide if I want to write something specific to this effort, or if the compiler-provided-default might be ok ... it often is. In which case I simply comment out one or more of the 6 delete lines, then compile again.

I resist commenting out these deletes. Sometimes it happens that one of my errors will trigger a use for one of the 6. Got to be watching for those, too.

  • Summary

These 6 lines (and there are other's you might choose to consider) will keep you informed of what methods the compiler is providing as a 'service'. (Most of the time, I dislike implicit conversions, too.)

So, instead of trying to understand why you might want or not want one these 6, this technique allows you find out what your program requires. Using each line (i.e. disabling at least long enough to see where and what for), the compiler complaints show where that 'move ctor' or 'move assignment' is being used. Very helpful when you know you didn't do it!

2785528
  • 5,438
  • 2
  • 18
  • 20