user13812739
user13812739

Reputation:

How to implement the same method in classes derived from an abstract base?

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:

  1. I want to keep Job abstract.

  2. Each Waiter or Builder has its own top_score value (It differs between classes and objects of the same class).

Upvotes: 2

Views: 561

Answers (3)

anastaciu
anastaciu

Reputation: 23802

You can define the method in the base class:

Live demo

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

Exagon
Exagon

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

Adrian Mole
Adrian Mole

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

Related Questions