Tiisje
Tiisje

Reputation: 27

Cannot instantiate abstract class, but double checked overriding of virtual functions

I'm doing self-study C++. I tried a program from the book, that would normally allocate a few objects of two derived classes dynamically using an array of pointers. I'm however preparing for an assignment where I'm not allowed to use pointers, so I made an alternative version without pointers.

The only error it gives me is C2259 "cannot instantiate abstract class", but I'm pretty sure I have overriden all the virtual functions.

Here is the header:

#ifndef ACCTBAC_H_
#define ACCTBAC_H_
#include <iostream>
#include <string>

// Abstract Base Class
class AcctABC
{
private:
    std::string fullName;
    long acctNum;
    double balance;
protected:
    struct Formatting
    {
        std::ios_base::fmtflags flag;
        std::streamsize pr;
    };
    const std::string& FullName() const { return fullName; }
    long AcctNum() const { return acctNum; }
    Formatting SetFormat() const;
    void Restore(Formatting& f) const;
public:
    AcctABC(const std::string& s = "Nullbody", long an = -1, double bal = 0.0);
    void Deposit(double amt);
    virtual void Withdraw(double amt) = 0;      // pure virtual function
    double Balance() const { return balance; };
    virtual void ViewAcct() const = 0;          // pure virtual function
    virtual ~AcctABC() {}
};

// Brass Account Class
class Brass : public AcctABC
{
public:
    Brass(const std::string& s = "Nullbody", long an = -1, double bal = 0.0) : AcctABC(s, an, bal) {}
    virtual void Withdraw(double amt);
    virtual void ViewAcct() const;
    virtual ~Brass() {}
};

// Brass Plus Account Class
class BrassPlus : public AcctABC
{
private:
    double maxLoan;
    double rate;
    double owesBank;
public:
    BrassPlus(const std::string& s = "Nullbody", long an = -1, double bal = 0.0, double ml = 500, double r = 0.10);
    BrassPlus(const Brass& ba, double ml = 500, double r = 0.1);
    virtual void ViewAcct() const;
    virtual void Withdraw(double amt);
    void ResetMax(double m) { maxLoan = m; }
    void ResetRate(double r) { rate = r; }
    void ResetOwes() { owesBank = 0; }
};
#endif

the class functions:

// acctabc.cpp -- bank account class methods
#include <iostream>
#include "acctabc.h"
using std::cout;
using std::ios_base;
using std::string;

// Abstract Base Class
AcctABC::AcctABC(const string& s, long an, double bal)
{
    fullName = s;
    acctNum = an;
    balance = bal;
}

void AcctABC::Deposit(double amt)
{
    if (amt < 0)
        cout << "Negative deposit not allowed; "
        << "deposit is cancelled.\n";
    else
        balance += amt;
}

void AcctABC::Withdraw(double amt)
{
    balance -= amt;
}

// protected methods for formatting
AcctABC::Formatting AcctABC::SetFormat() const
{
    // set up ###.## format
    Formatting f;
    f.flag = cout.setf(ios_base::fixed, ios_base::floatfield);
    f.pr = cout.precision(2);
    return f;
}

void AcctABC::Restore(Formatting& f) const
{
    cout.setf(f.flag, ios_base::floatfield);
    cout.precision(f.pr);
}

// Brass methods
void Brass::Withdraw(double amt)
{
    if (amt < 0)
        cout << "Withdrawal amount must be positive; "
        << "withdrawal cancelled.\n";
    else if (amt <= Balance())
        AcctABC::Withdraw(amt);
    else
        cout << "Withdrawal amount of $" << amt
        << " exceeds your balance.\n"
        << "Withdrawal cancelled.\n";
}

void Brass::ViewAcct() const
{
    Formatting f = SetFormat();

    cout << "Brass Client: " << FullName() << "\n";
    cout << "Account Number: " << AcctNum() << "\n";
    cout << "Balance: $" << Balance() << "\n";
    Restore(f);
}

// BrassPlus methods
BrassPlus::BrassPlus(const string& s, long an, double bal, double ml, double r) : AcctABC(s, an, bal)
{
    maxLoan = ml;
    owesBank = 0.0;
    rate = r;
}

void BrassPlus::ViewAcct() const
{
    Formatting f = SetFormat();

    cout << "BrassPlus Client: " << FullName() << "\n";
    cout << "Account Number: " << AcctNum() << "\n";
    cout << "Balance: $" << Balance() << "\n";
    cout << "Maximum loan: $" << maxLoan << "\n";
    cout << "Owed to bank: $" << owesBank << "\n";
    cout.precision(3);
    cout << "Loan Rate: " << 100 * rate << "%\n";
    Restore(f);
}

void BrassPlus::Withdraw(double amt)
{
    Formatting f = SetFormat();

    double bal = Balance();
    if (amt <= bal)
        AcctABC::Withdraw(amt);
    else if (amt <= bal + maxLoan - owesBank)
    {
        double advance = amt - bal;
        owesBank += advance * (1.0 + rate);
        cout << "Bank Advance: $" << advance << "\n";
        cout << "Finance charge: $" << advance * rate << "\n";
        Deposit(advance);
        AcctABC::Withdraw(amt);
    }
    else
        cout << "Credit limit exceeded. Transaction cancelled.\n";
    Restore(f);
}

and the main program:

// usebrass3.cpp -- polymorphic example using an abstract base class
#include <iostream>
#include <string>
#include "acctabc.h"
#include <vector>
const int CLIENTS = 4;

int main()
{
    using std::cin;
    using std::cout;
    using std::vector;
    using std::string;

    vector<AcctABC> accounts(CLIENTS);
    string temp;
    long tempnum;
    double tempbal;
    char kind;

    for (int i = 0; i < CLIENTS; i++)
    {
        cout << "Enter client's name: ";
        getline(cin, temp);
        cout << "Enter client's account number: ";
        cin >> tempnum;
        cout << "Enter opening balance: $";
        cin >> tempbal;
        cout << "Enter 1 for Brass Account: ";
        while (cin >> kind && (kind != '1' && kind != '2'))
            cout << "Enter either 1 or 2: ";
        if (kind == 1)
            accounts.push_back(Brass(temp, tempnum, tempbal));
        else
        {
            double tmax, trate;
            cout << "Enter the overdraft limit: $";
            cin >> tmax;
            cout << "Enter the interest rate "
                << "as a decimal fraction: ";
            cin >> trate;
            accounts.push_back(BrassPlus(temp, tempnum, tempbal, tmax, trate));
        }
        while (cin.get() != '\n')
            continue;
    }
    cout << "\n";
    for (int i = 0; i < CLIENTS; i++)
    {
        accounts[i].ViewAcct();
        cout << "\n";
    }
    cout << "Done.\n";

    return 0;
}

Upvotes: 0

Views: 390

Answers (2)

Cory Kramer
Cory Kramer

Reputation: 118001

You cannot do this

vector<AcctABC> accounts(CLIENTS);

because it will make CLIENTS number of default constructed abstract base classes. You will also lose polymorphism and induce object slicing. Instead

vector<std::unique_ptr<AcctABC>> accounts;

then for example

accounts.push_back(std::make_unique<Brass>(temp, tempnum, tempbal));

Upvotes: 1

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 123139

Here:

vector<AcctABC> accounts(CLIENTS);

you are trying to create a vector of AcctABC with CLIENTS default constructed AcctABC elements. But you cannot have AcctABC elements when the class is abstract. You also cannot have Brass elements in a vector of AcctABCs. You need pointers when you want to store a polymorphic type in a vector.

Upvotes: 2

Related Questions