C++: How to force class users to override at the same time get the benefit of delegation to the base class ?
Here is the practical use case where Abstract class instantiation is not allowed at the same time making use of Abstract class default implementation.
Walkthrough the example C++ slowly and carefully,
/******************** Account.hpp ********************/
#include <iostream>
class Account{
private:
static constexpr const char *def_name = "Unnamed Account";
static constexpr double def_balance = 0.0;
protected:
std::string name;
double balance;
public:
Account(std::string name = def_name, double balance = def_balance);
virtual bool deposit(double amount) = 0 ;
virtual bool withdraw(double amount) = 0 ;
double get_balance() const ;
virtual ~Account() = default;
};
/******************** Account.cpp ********************/
Account::Account(std::string name, double balance)
:name(name), balance(balance) {
}
bool Account::deposit(double amount) {
std::cout << "Inside Account deposit()" << std::endl;
if(amount < 0) {
return false;
}
else {
balance = balance + amount;
return true;
}
}
bool Account::withdraw(double amount) {
if (balance >= amount) {
balance = balance - amount;
return true;
} else {
return false;
}
}
double Account::get_balance() const {
return balance;
}
/******************** Savings_Account .hpp ********************/
class Savings_Account : public Account {
private:
static constexpr const char *def_name = "Unnamed Savings Account";
static constexpr double def_balance = 0.0;
static constexpr double def_int_rate = 0.0;
protected:
double int_rate;
public:
Savings_Account(std::string name = def_name, double balance = def_balance, double int_rate = def_int_rate);
virtual bool deposit(double amount) override;
virtual bool withdraw(double amount) override ;
virtual ~Savings_Account() = default;
};
/******************** Savings_Account .cpp ********************/
Savings_Account::Savings_Account(std::string name, double balance, double int_rate)
:Account(name, balance), int_rate(int_rate) {
}
bool Savings_Account::deposit(double amount) {
std::cout << "Inside Savings_Account deposit()" << std::endl;
amount = amount + (amount * (int_rate / 100));
return Account::deposit(amount);
}
bool Savings_Account::withdraw(double amount) {
return Account::withdraw(amount);
}
/******************** main.cpp ********************/
int main(){
//Account acc("Vijay", 1000); // This is Abstract class, instanting is forbidden
Account *s_acc_ptr = new Savings_Account("Tamil", 4000, 5);
s_acc_ptr->deposit(1000);
s_acc_ptr->withdraw(500); // Correctly called due to virtual
std::cout << "balance is "<< s_acc_ptr->get_balance() <<std::endl;
return 0;
}
here, Account class is Abstract class due to holding of 2 pure virtual functions. So creating the object of this Abstract class is not allowed by compiler.
virtual bool deposit(double amount) = 0 ;
virtual bool withdraw(double amount) = 0 ;
At the same time, these methods are provide with default definition in the same Abstract class which can be called by derived class as a delegation.
In Saving_Account constructor "name" and "balance" initialization by delegating to its Abstract base class.
Recommended by LinkedIn
Similarly Saving_Account::withdraw() is delegated to Abstract base class's Account::withdraw () as there is no need of separate logic in derived class.
Savings_Account::Savings_Account(std::string name, double balance, double int_rate)
:Account(name, balance), int_rate(int_rate) {
}
// AND
bool Savings_Account::withdraw(double amount) {
return Account::withdraw(amount);
}
Last one, Savings_Account::deposit() add its own business logic and reuse Account::deposit() offered by the Abstract class
bool Savings_Account::deposit(double amount) {
std::cout << "Inside Savings_Account deposit()" << std::endl;
amount = amount + (amount * (int_rate / 100));
return Account::deposit(amount); // Delegation to Base class
}
Conclusion:
So, We are reusing the base class implementation at the same time ensuring Abstract class is not instantiateable.