Reputation:
I have an abstract class Job
and other classes that implement it like:
Waiter
and Builder
, all of them implement my function in the same way.
For example:
Waiter::changeScore()
{
score += top_score;
}
How may I prevent this kind of code duplication?
Constraints:
I want to keep Job
abstract.
Each Waiter
or Builder
has its own top_score
value (It differs between classes and objects of the same class).
Upvotes: 2
Views: 561
Reputation: 23802
You can define the method in the base class:
class Job {
private:
int score;
int top_score;
protected:
//protected constructor to be inherited by derived classes
Job(int top_score) : top_score(top_score) {}
//one pure virtual method is enough to make the class abstract
virtual void some_method() = 0;
public:
void changeScore() { //single method implementation
score += top_score;
}
//to use polymorphism you must use a virtual destructor, unless you use shared_ptr
virtual ~Job(){}
};
class Waiter : public Job {
public:
Waiter(int top_score) : Job(top_score) {}
// pure virtual methods must be overridden in all derived classes
void some_method() override{}
};
class Builder : public Job {
public:
Builder(int top_score) : Job(top_score) {}
void some_method() override{}
};
changeScore()
will be implemented in the abstract class and will be usable by all derived classes.
Waiter w(10); //top_score 10
Buider b(20); // top_score 20
b.changeScore();
w.changeScore();
Upvotes: 1
Reputation: 5088
You can make the changeScore
method a pure virtual method AND provide an implementation. This would look like this:
class Job {
int score{0};
int top_score{0};
public:
virtual void changeScore() = 0;
};
void Job::changeScore()
{
score += top_score;
}
Then you can call the changeScore
method of the Job
base class in the child classes like this:
class Waiter : public Job {
public:
virutal void changeScore() override {
Job::changeScore();
}
};
This way if you want to change changeScore
, you do not need to change all the implementations in the child classes, but you can just change the implementation in the Job
class.
This way you do not need any dummy methods and the Job
class remains abstract, while the override in the child classes is trivial and you have a single implementation if you ever want to change it.
EDIT:
If you are wondering where this override
keyword comes from, it is introduced in C++11. Since I do not know which C++ version you are using I just wanted to point that out.
You can read about the override specifier here
EDIT II:
Regarding that ever child class has its own top_score
, you should set this via the constructor of those child classes.
Like this:
class Job {
protected:
int top_score{0};
Job(top) : top_score(top) {}
...
};
class Waiter : public Job {
public:
Waiter(int top): Job(top) {}
...
};
This way each child class has its own version of top_score
EDIT III: Putting it all together the classes would look something like this:
class Job {
protected:
int score{0};
int top_score{0};
Job(top) : top_score(top) {}
public:
virtual void changeScore() = 0;
};
void Job::changeScore()
{
score += top_score;
}
class Waiter : public Job {
public:
Waiter(int top): Job(top) {}
virutal void changeScore() override {
Job::changeScore();
}
};
Upvotes: 0
Reputation: 51835
Not all member functions of an abstract class need to be pure virtual (as long as at least one is). Your changeScore
member is an ideal candidate as a 'real' base class function. Further, not only does it not need to be pure virtual
, it doesn't even need to be virtual
at all (unless you want your polymorphism to change what a pointer to a derived class will see, for that function).
As each class (or object) will have its own value of top_score
(as you have stated), then that (data) member can also be part of the 'abstract' base class.
You can even add a single 'dummy' pure virtual function in your base class (which is never intended to be used, even by a derived class), just to make sure that instances aren't accidentally created. For example, your Job
class could have a member:
virtual int Dummy() = 0;
Then, any derived class must have an override for that (however trivial), or the compiler won't allow you to declare an instance of that class. So, your Waiter
class would need something like:
int Dummy override { return 1; }
The following code sample may help/demonstrate the idea:
#include <iostream>
#include <memory> // So we can use smart pointers
class Job {
public:
int score{ 0 }, top_score{ 0 };
public:
Job() { }
virtual ~Job() = default;
virtual void Dummy() = 0; // This is sufficient to make the class abstract!
void changeScore() {
score += top_score;
}
virtual void showName() {
std::cout << "Generic Job" << std::endl;
}
};
class Waiter : public Job {
public:
Waiter(int top = 5) { top_score = top; }
~Waiter() override = default;
void Dummy() override { } // We need this in order to use Waiter
void showName() override {
std::cout << "Waiter" << std::endl;
}
};
class Builder : public Job {
public:
Builder(int top = 10) { top_score = top; }
~Builder() override = default;
void Dummy() override { } // We need this in order to use Builder
void showName() override {
std::cout << "Builder" << std::endl;
}
};
int main()
{
Waiter w{ 6 }; // OK - uses explicit value for 'top' parameter
Builder b; // OK - uses default value for 'top' parameter
// Job j; // ERROR - Cannot instantiate abstract class
w.changeScore();
b.changeScore();
std::cout << w.score << std::endl;
std::cout << b.score << std::endl;
// Also, using pointers...
// Job* pj = new Job; // ERROR - Cannot instantiate abstract class
Job* pw = new Waiter; // OK - Now we can make use of polymorphism...
Job* pb = new Builder; // ...with either of these 2 "Job" pointers!
pw->showName();
pb->showName();
delete pw;
delete pb;
// Polymorphism also works with smart pointers (which you SHOULD be using) ...
// std::unique_ptr<Job> upj = std::make_unique<Job>(); // ERROR - Allocating an object of abstract class
std::unique_ptr<Job> upw = std::make_unique<Waiter>(15);
upw->changeScore();
std::cout << upw->score << ": ";
upw->showName();
std::unique_ptr<Job> upb = std::make_unique<Builder>(42);
upb->changeScore();
std::cout << upb->score << ": ";
upb->showName();
return 0;
}
Upvotes: 2