'Pure Abstract Base Class'

Hello Community,

Once again I kindly ask for help with the following problem. My book's current assignment is titled: "Pure Abstract Base Class Project."

My first question is, is there even a concept of 'Pure Abstract Base Class' in C++?

What I know: In C++ an abstract class is one which cannot be instantiated that implements at least one pure virtual function that must be overridden in any class that derives from it. (I am not sure whether it becomes an abstract class by virtue of adding a pure virtual destructor, while all the rest is concrete methods).

Concrete example and actual code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef BASIC_SHAPE_H_
#define BASIC_SHAPE_H_

#include <iostream>

class BasicShape
{
	protected:
		double area;		// Holds the shape's area

	public:
		BasicShape();
		virtual ~BasicShape() = 0;

                // Concrete member function
		double getArea();
		virtual void calcArea() = 0;
};

#endif 


So, if there is a concept of 'Pure Abstract Base Class', of what little I found about the topic, it should not have any concrete methods, or member variables for that matter, it should consist of only virtual methods (besides the *constructor which can't be virtual, as there is no such thing as virtual constructor - correct?)

So, to make the above a 'Pure Abstract Base Class', or 'Interface?' it would have to look more like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef BASIC_SHAPE_H_
#define BASIC_SHAPE_H_

#include <iostream>

class BasicShape
{
	public:
		BasicShape();
		virtual ~BasicShape() = 0;

		virtual double getArea() const = 0;

		// Pure virtual function
		virtual void calcArea() = 0;
};

BasicShape::BasicShape()
{}

BasicShape::~BasicShape()
{}

#endif 


All that is in this class has then to be overridden in any derived class and implemented there. Correct?

-

Now, back to the concrete example at hand, the BasicShape class, and some questions concerning it. Since it contains a member variable, and one member, that - according to the problem statement should be private instead of having the access specifier protected. This, now, is my second question.

If I were to have the member access specifier in the abstract class set to private, and without adding any additional methods to the class;

How - besides declaring this derived class a 'friend' - Which i guess is the only way to give the derived class access to base class's private member, which - if it is correct, would make the concept of deriving obsolete, to give the derived class access to this private member variable? (If at all possible in such scenario).

Knowing that no class derived or otherwise can gain access to base class private member, or alter it in any way. A real problem right there.

To be totally honest, I find that the problem statement as it is does make little sense. For instance, if it were that there is only one pure virtual function to override:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[code]
#ifndef BASIC_SHAPE_H_
#define BASIC_SHAPE_H_

#include <iostream>

class BasicShape
{
	protected:
		double area;		// Holds the shape's area

	public:
		BasicShape();
		virtual ~BasicShape() = 0;
		virtual double calcArea() = 0;
};

BasicShape::~BasicShape()
{ 
}

#endif 


Derived Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 
#ifndef CIRCLE_H_
#define CIRCLE_H_

#include "BasicShape.h"

class Circle : public BasicShape
{
	private:
		long centerX;			// Holds the x coordinate of the circle's center
		long centerY;			// Holds the y coordinate of the circle's center
		double radius;			// Holds the circle's radius

	public:
		// Constructor
		Circle(long, long, double);

		~Circle()
		{ }

		long   getCenterX() const;
		long   getCenterY() const;
		double getRadius() const;

		virtual double calcArea();
};

#endif 


Implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include "Circle.h"

#include <cmath>

Circle::Circle(long x, long y, double r) : BasicShape()
{
	centerX = x;
	centerY = y;
	radius = r;

	calcArea();
}

double Circle::calcArea()
{
	const double PI = 3.1415927;

	return (area = PI * (pow(radius, 2.0)));
}

long Circle::getCenterX() const
{ 
	return centerX;
}

long Circle::getCenterY() const
{
	return centerY;
}

double Circle::getRadius() const
{
	return radius;
}


Would do it all, if my code and the way it is written is correct. centerX and centerY, besides being there, they are only there for having something to output, which I will like add code to do some actual calculations on it. There is no mention of it in the problem statement, but this is a case in which I willingly deviate. (One of the rather rare cases.)

* Constructor: Here I do have one final question. Does it even make sense to have a constructor in the abstract (or pure abstract version)? Besides, for instance having another that accepts parameters, where it would make sense to have a Constructor? (I hope the question makes sense).

Any helpful answer to any of the questions, as well as critique, is highly welcome and appreciated! (also links to resources, books/online, if you have any , please share!)
My first question is, is there even a concept of 'Pure Abstract Base Class' in C++?


Yes, where all the functions are pure virtual functions.

One shouldn't have protected data, the same arguments against public data apply to protected data also.

I like to name my headers with a hpp extension, it means header with cpp code inside.

With the comments, avoid stating the obvious. Good code should tell a story with well named variables and functions.

With the area, one can calc it every time and return a value, or if there is a getArea function, then something changes that function must be responsible for updating the area variable.


Edit: Fixed some typos, it's not entirely complete though.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#ifndef BASIC_SHAPE_HPP_
#define BASIC_SHAPE_HPP_

#include <iostream>

#include "Point.hpp"

class BasicShape
{
	//protected:  // protected data is bad.
        // protected functions are OK though
        protected:
               void SetArea(double TheArea)  ; // put defintion in cpp file
        private:
		double area {};		// Holds the shape's area unhelpful comment
                double perimeter {}; // initialise to 0.0 avoid ctor

	public:
		BasicShape(); // if no ctor supplied a default is used by compiler
		virtual ~BasicShape() = 0;
		virtual double CalcArea() = 0;
	         virtual double CalcPerimeter() = 0;

                double GetArea()  const ;
                 
                // some fun functions likely to be in this class
                virtual void Move(const Point& NewLocation) = 0;  
                virtual void Copy(const Point& NewLocation) = 0;   
                virtual void Rotate(const double RotationAngle) = 0;
};

BasicShape::~BasicShape()
{ 
}

#endif  


1
2
3
4
5
6
7
#ifndef MATH_CONSTANTS_HPP_

#include <cmath>

namespace MathConstants {
     constexpr double PI =  arcos(-1.0);  // constexpr, set in stone at compile time
};


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#ifndef CIRCLE_HPP_
#define CIRCLE_HPP_

#include "BasicShape.hpp"
#include "MathConstants.hpp"


class Circle : public BasicShape
{
	private:
		//long centerX;			// Holds the x coordinate of the circle's center
		//long centerY;			// Holds the y coordinate of the circle's center
                Point Centre;            
		double Radius;			// Invariant, radius must be positive

	public:
		// Constructor
                // I like this format for multiple arguments, always put a variable name for each argument
		Circle(double CentreXArg, 
                          double CentreYArg, 
                          double RadiusArg
                )
                :   
                Centre( CentreXArg, CentreYArg),
                Radius {RadiusArg}
                { // do validation here
                  if (Radius <= 0.0)  {// should throw exception here, but that's another story
                      Radius *= -1.0; 
                  }

                  BasicShape::SetArea(CalcArea() );
                }

		

		const Point&  getCenter() const;
		
		double getRadius() const;

		virtual double CalcArea() override ;  // override tells the compiler we are over riding the virtual function
		virtual double CalcPerimeter() override ;
};

#endif  



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include "Circle.hpp"

#include <cmath>

// already have defined constructor
Circle::Circle(long x, long y, double r) : BasicShape()
{
	centerX = x;
	centerY = y;
	radius = r;

	calcArea();  // looks dodgy - has no effect? , nothing done with return value, see above
}

double Circle::calcArea()
{
	const double PI = 3.1415927;

	return (area = PI * (pow(radius, 2.0))); // pow is expensive for squaring
       
        SetArea(MathConstants::PI * radius * radius ); // calls BasicShape::SetArea because it's inherited

        return GetArea();  // a simple return statement makes it easier for debugging
}

long Circle::getCenterX() const
{ 
	return centerX;
}

long Circle::getCenterY() const
{
	return centerY;
}


const Point&  Circle::getCenter() const 
{
  return Centre;
}

double Circle::getRadius() const
{
	return radius;
}


With class design one has to decide what is going to be in the public interface, what should be private? Notice I had the protected SetArea function because it would wrong for anyone to change that value at will.

Be careful not to fall into the trap of having getters and setters for every member variable, if so it may as well be a fully public struct.

With abstract classes, have a read of this:

https://stackoverflow.com/questions/19808667/c-abstract-class-constructor-yes-or-no#19808791

In the above code I didn't have a ctor, setting a value in the class declaration is the same as if there was a ctor doing the same thing - it saves a lot of typing :+)

About Interfaces:

https://stackoverflow.com/questions/318064/how-do-you-declare-an-interface-in-c

Advanced:
gotw.ca/publications/mill18.htm.

Good Luck !!

Last edited on
This is what a 'pure' abstract class (a 'pure' object-oriented interface) would look like in C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 'pure' interface aka 'pure' abstract base class
// note 1: optionally, may inherit from one or more 'pure' interfaces
struct shape // note 2: does not have any implementation; so all members
             //         are public (there is no implementation to hide)
{
    // implicitly declared default constructor

    // note 3: explicitly defaulted virtual destructor, copy/move constructors,
    // copy/move assignment operators
    // When a base class is intended for polymorphic use, its destructor may have
    // to be declared public and virtual. This blocks implicit moves
    // (and deprecates implicit copies), and so the special member functions
    // have to be declared as defaulted
    // http://en.cppreference.com/w/cpp/language/rule_of_three
    virtual ~shape() = default ;
    shape( const shape& ) = default ;
    shape( shape&& ) noexcept = default ;
    shape& operator=( const shape& ) = default ;
    shape& operator=( shape&& ) noexcept = default ;

    // note 4: all operations are polymorphic and not implemented
    //         (unimplemented pure virtual functions) eg.
    virtual double area() const noexcept = 0 ;
    // virtual .....   = 0 ;
    // etc.

    // note 5: the class may contain nested types or type aliases
    //         which are used in its interface eg.
    enum colour { BLACK = 0, RED = 1, GREEN = 2, BLUE = 4, WHITE = RED|GREEN|BLUE, YELLOW = RED|GREEN /* etc */ } ;
    virtual void fill( colour clr ) = 0 ;
};




This, even after the typo is corrected, wont compile with a conforming implementation:
1
2
3
4
5
#include <cmath>

namespace MathConstants {
     constexpr double PI =  std::acos(-1.0);  // constexpr, set in stone at compile time
}

http://coliru.stacked-crooked.com/a/eef1e83e11262462

This document explicitly requires that certain standard library functions are constexpr. An implementation shall not declare any standard library function signature as constexpr except for those where it is explicitly required.
http://eel.is/c++draft/constexpr.functions


This is fine:
1
2
3
4
5
namespace maths {

     template < typename T = double >
         constexpr T PI =  T( 3.1415926535897932384626433832795028841972) ;
}
@JLBorges

Thanks for picking that up, I will learn to read and type, one day :+D !

My original code is a thing that works under g++(7.3.1), but clang++ (v5.01) does the right thing according to the standard. It is good to find out about it, there were numerous places I had this in code. And a reminder to compile everything with different compilers: I would have discovered that for myself.

And I should have compiled the code I posted.

However this does seem to work :

1
2
3
namespace maths {
   const double pi = acos(-1.0);
}




I guess we could use boost:

http://boost-sandbox.sourceforge.net/libs/math_constants/doc/html/

TheIdeasMan, thank you very much! Not only for the resources, also for the code example!

True, the comments, ... It is stupid, and one of the few things difficult to get rid of completely. :) Same with over-commenting. For a long time there was this habit to comment each and everything, from variables to what a return statement returns, ... GUILTY!

I was not aware that having setters and getters in for every member variable should be avoided. In almost all examples in my book there is an equal number of accessors and mutators.

Much less that one should not have protected data ... This, so I thought until reading your advice, is, what need be there in order to make a/the member variable accessible by a derived class. Again, the examples all have protected as access specifier in the base class. The only deviation is this one problem here. It explicitly says: 'Private member variable', it is anyone's guess that this could also mean - use also a setter, without mentioning it explicitly.

--

Also, in your code, does this really work?

 
return GetArea();  // a simple return statement makes it easier for debugging 


I seem to remember having read something along the lines of: 'The return type of a function cannot be a function type'. Would you explain, please?

 
virtual double CalcArea() override ;  // override tells the compiler we are over riding the virtual function 


Thank you for pointing this one out!

--

You noticed the function call in the constructor. It is my fault. While expermenting, I declared the pure virtual function as having a return type of double. Which, at least in my humble opinion, would make more sense instead of having a member variable and a getter. Which, and this is yet another version I have written in the last couple of hours, would make the base class look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef BASIC_SHAPE_H_
#define BASIC_SHAPE_H_

class BasicShape
{
	public:
		virtual ~BasicShape() = 0;

		// The name is up for discussion :-)
		virtual double calcArea() = 0;
};

#endif 


Having it like this, there would be no need for the accessor in the base class. It also makes having the 'private member variable' in the base class obsolete. (At least in my humble opinion).

--

This is part of the problem statement:

Define a pure abstract base class called BasicShape.

The BasicShape class should have the following members:

Private Member Variable
area, a double used to hold the shape's area.

Public Member Functions
getArea. This function should return the value in the member variable area.
calcArea. This function should be a pure virtual function.


Circle class:

Public Member Functions
constructor—accepts values for centerX, centerY, and radius. Should call
the overridden calcArea function described below. (Add: Which should calculate the area and assign the result to the base class's inherited member variable.)


--

The problem statement, it feels poorly thought out, the 'Pure Abstract Base Class' being a misnomer, translating to: 'Abstract Base Class with Pure Virtual Function', the private access specifier that should really have been protected access specifier etc. This is why, in this case, I will ignore the problem statement in part, and take your code as guideline to solve it in a way that makes sense.

Minus:

// should throw exception here, but that's another story

Which is the topic of the upcoming chapter in my book. (And to mention the title: Starting Out With C++ From Control Structures to Objects 9th edition)
Last edited on
JLBorges thank you for providing an example of what makes up a 'pure' virtual base class. Much appreciated!

Also, in your code, does this really work?

return GetArea(); // a simple return statement makes it easier for debugging

I seem to remember having read something along the lines of: 'The return type of a function cannot be a function type'. Would you explain, please?


GetArea() returns a double, so the statement becomes in pseudo code return a value of type double


Along with override, there is final
http://en.cppreference.com/w/cpp/language/final


Having it like this, there would be no need for the accessor in the base class. It also makes having the 'private member variable' in the base class obsolete. (At least in my humble opinion).


Imagine a system where the radius can be changed via Scale function. How often is it changed? How often is are these values queried? Should we calculate the Area every time asked? Or should we store the Area in a variable and just return it's value when asked?

... the private access specifier that should really have been protected access specifier etc.


I disagree. It seems easy to make the data protected, one can access it easily from the derived classes. But have a read of this:

https://softwareengineering.stackexchange.com/questions/162643/why-is-clean-code-suggesting-avoiding-protected-variables

Then there is this:

https://isocpp.org/wiki/faq/basics-of-inheritance#access-rules

It throws what I just said in my face, except for this part:

isocpp wrote:
The benefit of protected access functions is that you won’t break your derived classes as often as you would if your data was protected
.

The above sites are well worth keeping as favourites, wiki and Google are useful tools as well.
GetArea() returns a double, so the statement becomes in pseudo code return a value of type double


Now, that is a great trick! I will remember it.

Along with override, there is final
http://en.cppreference.com/w/cpp/language/final


Yes, this is also found in my book's current chapter, saying:

C++ 11 introduces the override key word to help prevent subtle errors when overriding virtual functions, given the example:

Base Class:
virtual void functionA(int arg) const;

Derived Class:
virtual void functionA(long arg) const;

Which, if override were used, this subtle error would prevent the program from running. Or, as is the case with Visual Studio, throws an error saying

'Derived': cannot instantiate abstract class


Having it like this, there would be no need for the accessor in the base class. It also makes having the 'private member variable' in the base class obsolete. (At least in my humble opinion).

Imagine a system where the radius can be changed via Scale function. How often is it changed? How often is are these values queried? Should we calculate the Area every time asked? Or should we store the Area in a variable and just return it's value when asked?


In such scenario the calculation should be carried out, and the result be stored in a variable and returned when asked. It is an important point I haven't taken into consideration.

Let me qualify my last statement.

I have drawn the conclusion, that, iff a class should be pure abstract, and this pure abstract class has a virtual function that does the calculation and returns the result, there is no need for having a 'private member variable' to hold the result of said calculation. Neither would there be need or reason for having a mutator function that is a concrete method.

It is the problem itself, viewing it as 'isolated', there will be no changes made to this project once it is finished and the problem solved. This is the reason why a thought about 'What, if ...' never occured to me.

... the private access specifier that should really have been protected access specifier etc.

I disagree. It seems easy to make the data protected, one can access it easily from the derived classes.


Wouldn't it be fair to say: 'Once you know and understand, it becomes easy.'? ;-)

As to your link, you would laugh if I told you that, shortly after reading and replying, I found this exact discussion over at stackexchange. I am also aware of the existence of isocpp, landed there while doing research about a particular subject. (And this is a wonderful resource, as it drives the point home by looking at it from various angles, and describes things in a way easy to understand.)

I also do my 'homework' if you will, researching topics, that - had I not set out on the journey to learn C++, probably would never have learned about. (Translating to: How often does a 'normal' person dig deeper into Spectral Analysis or learning about it?) Programming, and solving problems programmatically, it opens up whole new worlds of interesting (more or less) topics to learn about, while trying to solve a problem.)

-

One thing is still left unanswered: 'HOW can the problem be solved, given the problem statement, WITHOUT introducing any new functions to the base class, by HAVING a 'private member variable', AND a set of derived classes that do assign a value to it? (Yes, you may say there is no mention of not introducing any more functions than those named in the problem statement; Yet, if there should be, it is mentioned explicitly in a NOTE section beneath the problem statement.)

If you find it is not worth to try, it would be a waste of your precious time to go looking for an answer as there simply is none, simply say - forget it -.

-

This is what I have changed the code to (Notice that I haven't introduced all of your ideas, no namespace (I haven't learned it and want to understand before going about using it.), because the important thing is to get the basics right. I also kept the variables according to the problem statement. This also applies to the methods. And of course no catch and/or exception (topic of next chapter) :-)):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef BASIC_SHAPE_H_
#define BASIC_SHAPE_H_

class BasicShape
{
	private:
		double area;

	protected:
		void setArea(double);

	public:
		virtual ~BasicShape() = 0;

		double getArea() const;

		virtual double calcArea() = 0;
};

#endif 


Implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "BasicShape.h"

BasicShape::~BasicShape()
{
}

void BasicShape::setArea(double ar)
{
	area = ar;
}

double BasicShape::getArea() const
{ 
	return area; 
}


Derived Class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#ifndef CIRCLE_H_
#define CIRCLE_H_

#include "BasicShape.h"

class Circle : public BasicShape
{
	private:
		long centerX;		
		long centerY;			
		double radius;			

	public:
		Circle::Circle(long x, long y, double r) : BasicShape()
		{
			centerX = x;
			centerY = y;
			radius = r;

			BasicShape::setArea(calcArea());
		}

		~Circle()
		{ }

		long   getCenterX() const;
		long   getCenterY() const;
		double getRadius() const;

		virtual double calcArea() override;
};

#endif 


Implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include "Circle.h"

double Circle::calcArea()
{
	setArea(3.1415927 * radius * radius);
	
	return getArea();
}

long Circle::getCenterX() const
{ 
	return centerX;
}

long Circle::getCenterY() const
{
	return centerY;
}

double Circle::getRadius() const
{
	return radius;
}
Last edited on
Now, that is a great trick! I will remember it.


More formally a return statement is:

return expression ;

the result of expression is what is returned.

I have drawn the conclusion, that, iff a class should be pure abstract, and this pure abstract class has a virtual function that does the calculation and returns the result, there is no need for having a 'private member variable' to hold the result of said calculation. Neither would there be need or reason for having a mutator function that is a concrete method.


I find that a little extreme, classes are usually about storing data and the functions which operate on that data.

TheIdeasMan wrote:
I disagree. It seems easy to make the data protected, one can access it easily from the derived classes.


Wouldn't it be fair to say: 'Once you know and understand, it becomes easy.'? ;-)


Perhaps I should have said "... one can access it directly ..."

With your current code, it is fine except there is no need to provide an implementation for the destructor of BasicShape, you are better off with the default one the compiler will provide.

More formally a return statement is:

return expression ;

the result of expression is what is returned.


Which is understood, thank you!

I find that a little extreme, classes are usually about storing data and the functions which operate on that data.

TheIdeasMan wrote:
I disagree. It seems easy to make the data protected, one can access it easily from the derived classes.


It seems to be, yet it is also not uncommon practice to do it. Here is an example:

1
2
3
4
5
6
7
8
9
// Vessel.h Abstract class defining a vessel
#ifndef VESSEL_H
#define VESSEL_H
class Vessel
{
    public:
       virtual double volume() const = 0;
};
#endif 


Derived class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Box.h
#ifndef BOX_H
#define BOX_H

#include "Vessel.h"

class Box : public Vessel
{
    protected:
       double length {1.0};
       double width {1.0};
       double height {1.0};

    public:
       Box(double lv, double wv, double hv) : length {lv}, width {wv}, height {hv} {}

       double volume() const override { return length*width*height; }
};
#endif 


I also found examples demonstrating how to do it like you did. Having private member variables, protected member functions, and public virtual ones in the base class.

What no source explains is: WHEN does it make sense to prefer one way over the other? Or: When should one way be avoided and WHY the other is the better choice. Something I consider important to be able to make an informed decision, but what can you do?


With your current code, it is fine except there is no need to provide an implementation for the destructor of BasicShape, you are better off with the default one the compiler will provide.


Thank you, it is now gone. And thank you one last time for your patience to answer my questions, and providing code to work with!
Hi,

One thing about class design: The idea is to have variables and functions appear once in a class hierarchy, but have them inherited. In other words we see what things are common, and push them higher up the tree. Things like Area and Perimeter are in the base class because they are common to all the derived classes. This means we may need to create a new base classes to accommodate new things added to our system.

For example, say we are designing a CAD system similar to Autodesk's AutoCAD but a lot more basic. We need to have things like Annotation, LineSegment, PolyLine (a series of lines/ curves as one entity), RegularPolygon, IrregularPolygon, Point (as a Drawing Entity in it's own right), Circle, Ellipse, Circular Arc, Elliptical Arc. Every entity is to have it's own Layer, Colour, and LineType. Every Drawing Entity is to have the capability to be Drawn, Erased, Moved, Copied, Rotated and Scaled.

See if you can modify your class hierarchy to suit these requirements. No need to write a shed load of code, just demonstrate the class tree with declarations and the virtual functions only (member variables if you want), at the moment we basically have :

1
2
3
4
5
6
7
8
9
class BasicShape {
  protected:
     void setArea(double theArea) 
 public:
   virtual getArea = 0;
};

class Circle : public BasicShape {
};


Some thoughts on this:

Pay attention to SOLID with this, especially the L. Think carefully about what defines a shape.

Some things will live in the base class but may not need to be a pure virtual function, some things may not need to be virtual at all. Some things might not be inherited at all. And there may be composition.

Can you think of some things which are not Drawing Entities, but still need to be in your class tree somewhere?

Go back to your code again:

Always provide a variable name for function parameters / arguments.
Don't abbreviate variable names too much. ar is not a good name. I do things like AreaArg to differentiate from Area.

You might have gathered that I am a fan of PascalCase but that is just a personal preference :+)
Can you imagine how glad this person feels for having started this topic? All because of your continuous effort to answer, to explain, to provide code and resource, and - on top of it all, you now provide an exercise. It is one with which you struck a strong chord, as I am familiar with Autodesk and Maya, as well as 3D Studio Max etc. and still do today. So this exercise made me realize several things, the most important being that I should have realized that there is a connection between the programming task (book) with something I already know.

Thinking and looking at it this way:
'Solve the problem, since there is not very much to think about besides how to write the class file, its members, functions, and to make sure the result of calculating the area of a circle object and a rectangle object is correct ...'

Now I truly have something to work on. :-)

-

Go back to your code again:

Always provide a variable name for function parameters / arguments.
Don't abbreviate variable names too much. ar is not a good name. I do things like AreaArg to differentiate from Area.

You might have gathered that I am a fan of PascalCase but that is just a personal preference :+)


This is something I usually do. Not doing it in this case may just be another sign of the fact that the problem had no meaning beyond solving it. Another 'first,' in a long series since starting with this problem.

And, yes, I noticed your preference. :)
'Solve the problem, since there is not very much to think about besides how to write the class file, its members, functions, and to make sure the result of calculating the area of a circle object and a rectangle object is correct ...'


I didn't mean for you to write code for everything: to start with just get the class design right. It is probably useful to draw a diagram of the classes. It should only take a few minutes to put it on paper or type the class declarations, though it may take a bit longer to think about them.

There are some clues, pay attention to the particular words I used.
Topic archived. No new replies allowed.