override specifier

Hello. In the next code, must be written the keyword override in the derived classes or not? It seems to me that the virtual keyword in the base Class is enough in order to allow a redefinition. However, I read that some prefer in the derived classes differents definitions :

virtual myFunction(arg) override { ... }
myFunction(arg) override { ... }
myFunction(arg) { ... }

All these alternatives work as expected, but I guess that some are better than others. Thanks for your help. Have a nice day ++

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <iostream>
// base class
class Volume {

protected:
    float a;
    float b;
    float c;

public:
    Volume(float a = 1, float b = 1, float c = 1) : a(a), b(b), c(c) {}
    virtual float computeVolume(void) = 0; // virtual function
    void printVolume();
 
    std::string name() const { return typeid(*this).name(); }
};

void Volume::printVolume()
{
    std::cout << this->name() << " : " << this->computeVolume() << std::endl;
}
/////////////////////////////////////////
// box class
class Box : public Volume {

public:
    Box(float l, float w, float h) : Volume(l, w, h) {}
    float computeVolume() override;
};
// new definition of the virtual function
float Box::computeVolume()
{
    std::cout << "Volume for a Box with size " << a << " " << b << " " << c << "\t -> ";
    return a * b * c;
}
/////////////////////////////////////////
// sphere class
class Sphere : public Volume {

public:
    Sphere(float r) : Volume(r) {}
    float computeVolume() override;
};
// new definition of the virtual function 
float Sphere::computeVolume()
{
    std::cout << "Volume for a Sphere with radius " << a << "\t -> ";
    return float(4 * 3.14 * a * a * a) / 3;
}
/////////////////////////////////////////
// square pyramid class
class sPyramid : public Volume {

public:
    sPyramid(float l, float h) : Volume(l, h) {}
    float computeVolume() override;
};
// new definition of the virtual function
float sPyramid::computeVolume()
{
    std::cout << "Volume for a Pyramid with size " << a << " " << b << "\t -> ";
    return ((float)1/3 * a * a * b);
}
/////////////////////////////////////////
int main() 
{
    Volume* v1 = new Box(4, 5, 6);
    Volume* v2 = new Sphere(3.7f);
    Volume* v3 = new sPyramid(4.2f, 5.5f);

    v1->printVolume();
    v2->printVolume();
    v3->printVolume();

    delete v1;
    delete v2;
    delete v3;

    return 0;
}



Volume for a Box with size 4 5 6         -> class Box : 120
Volume for a Sphere with radius 3.7      -> class Sphere : 212.067
Volume for a Pyramid with size 4.2 5.5   -> class sPyramid : 32.34
Last edited on
I favour: virtual myFunction( int arg ) override ;

Though this violates CoreGuidelines recommendation:
C.128: Virtual functions should specify exactly one of virtual, override, or final
http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rh-override
An override on a derived class function ensures that you are actually overriding a base class function - and not defining a new one eg by accident. You don't specify override on a base class function.

virtual - if needed - is applied to the base function. It can (but doesn't need to be) applied to derived class functions. A base virtual final function spec makes no sense at all!
Last edited on
> A virtual final function spec makes no sense at all!

It is fine, even if it conveys superfluous, (non-contradictory) information:

1
2
3
struct A { virtual void foo() const noexcept = 0 ; };

struct B : A { virtual void foo() const noexcept override final { /* ... */ } };

https://coliru.stacked-crooked.com/a/12cbda755291c28f
Thank you for your advices and links.
The Core Guidelines seems interesting.
Is it your favorite reference when there is a question or a doubt?
Last edited on
It is fine, even if it conveys superfluous, (non-contradictory) information:


I meant:

1
2
3
struct A {
	virtual void foo() final {};
};


[Changed my reply above]
Last edited on
> Is it your favorite reference when there is a question or a doubt?

It is a useful set of guidelines, easily available to everyone.
Perhaps unnecessarily strict and too dogmatic; hopefully over a period of time, it would also cover programming situations which warrant a relaxation of some of these rules.
Last edited on
 
virtual float computeVolume(void) = 0; // virtual function 


You don't need void here in C++. You do:

 
virtual float computeVolume() = 0; // virtual function 


printVolume() and computeVolume() should be const as they don't change the member variables.

Without a virtual Volume destructor, compiler (at least VS) spews out warnings.

Why use float and not double?

Since C++20 common constants (like pi) are defined.
https://en.cppreference.com/w/cpp/numeric/constants

Note that pi etc (without the _v) are defined for double. So for float we need the _v version with a specified type.

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <iostream>
#include <numbers>

// base class
class Volume {
protected:
	float a;
	float b;
	float c;

public:
	Volume(float a = 1, float b = 1, float c = 1) : a(a), b(b), c(c) {}
	virtual float computeVolume() const = 0; // virtual function
	void printVolume() const;

	std::string name() const {
		return typeid(*this).name();
	}

	virtual ~Volume() {}    // To avoid compiler warnings!
};

void Volume::printVolume() const {
	std::cout << name() << " : " << computeVolume() << '\n';
}

/////////////////////////////////////////
// box class
class Box : public Volume {
public:
	Box(float l, float w, float h) : Volume(l, w, h) {}
	float computeVolume() const override;
};

// new definition of the virtual function
float Box::computeVolume() const {
	std::cout << "Volume for a Box with size " << a << " " << b << " " << c << "\t -> ";
	return a * b * c;
}

/////////////////////////////////////////
// sphere class
class Sphere : public Volume {
public:
	Sphere(float r) : Volume(r) {}
	float computeVolume() const override;
};

// new definition of the virtual function
float Sphere::computeVolume() const {
	std::cout << "Volume for a Sphere with radius " << a << "\t -> ";
	return float(4.0f * std::numbers::pi_v<float> * a * a * a) / 3.0f;
}

/////////////////////////////////////////
// square pyramid class
class sPyramid : public Volume {
public:
	sPyramid(float l, float h) : Volume(l, h) {}
	float computeVolume() const override;
};

// new definition of the virtual function
float sPyramid::computeVolume() const {
	std::cout << "Volume for a Pyramid with size " << a << " " << b << "\t -> ";
	return (a * a * b) / 3.0f;
}

/////////////////////////////////////////
int main() {
	const Volume* v1 { new Box(4, 5, 6) };
	const Volume* v2 { new Sphere(3.7f) };
	const Volume* v3 { new sPyramid(4.2f, 5.5f) };

	v1->printVolume();
	v2->printVolume();
	v3->printVolume();

	delete v1;
	delete v2;
	delete v3;
}

Last edited on
Re. virtual destructor
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
...
however, this makes the class prone to slicing, which is why polymorphic classes often define copy as deleted which leads to the following generic wording for the Rule of Five:
C.21: If you define or =delete any default operation, define or =delete them all
https://en.cppreference.com/w/cpp/language/rule_of_three#Rule_of_zero
Topic archived. No new replies allowed.