【问题标题】:Cannot instantiate abstract class, but double checked overriding of virtual functions无法实例化抽象类,但仔细检查了虚函数的覆盖
【发布时间】:2020-10-16 03:43:12
【问题描述】:

我正在自学 C++。我尝试了书中的一个程序,该程序通常会使用指针数组动态分配两个派生类的一些对象。但是,我正在准备一项不允许使用指针的作业,所以我制作了一个没有指针的替代版本。

它给我的唯一错误是 C2259“无法实例化抽象类”,但我很确定我已经覆盖了所有虚函数。

这是标题:

#ifndef ACCTBAC_H_
#define ACCTBAC_H_
#include 
#include 

// 抽象基类
AcctABC 类
{
私人的:
    标准::字符串全名;
    长账号;
    双重平衡;
受保护:
    结构格式化
    {
        std::ios_base::fmtflags 标志;
        标准::流大小公关;
    };
    const std::string& FullName() const { return fullName; }
    long AcctNum() const { return acctNum; }
    格式化 SetFormat() const;
    无效恢复(格式化& f)常量;
民众:
    AcctABC(const std::string& s = "Nullbody", long an = -1, double bal = 0.0);
    无效存款(双倍);
    虚拟无效取款(双倍)= 0; // 纯虚函数
    double Balance() const { 返回余额; };
    虚拟 void ViewAcct() const = 0; // 纯虚函数
    虚拟 ~AcctABC() {}
};

// 黄铜帐户类
黄铜类:公共AcctABC
{
民众:
    黄铜(const std::string& s = "Nullbody", long an = -1, double bal = 0.0) : AcctABC(s, an, bal) {}
    虚拟无效取款(双倍);
    虚拟 void ViewAcct() 常量;
    虚拟〜黄铜(){}
};

//黄铜加帐户类
BrassPlus 类:公共 AcctABC
{
私人的:
    双倍最大贷款;
    双倍费率;
    双欠银行;
民众:
    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);
    虚拟 void ViewAcct() 常量;
    虚拟无效取款(双倍);
    void ResetMax(double m) { maxLoan = m; }
    无效 ResetRate(double r) { rate = r; }
    无效 ResetOwes() { owesBank = 0; }
};
#万一

类函数:

// 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);
}

和主程序:

// 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;
}

【问题讨论】:

  • 您不能将Brass 存储在AcctABC 的向量中。阅读对象切片。
  • 所以,What is object slicing? 的另一个骗子 - tl;dr:你想要(a)std::vector&lt; std::unique_ptr&lt;AcctABC&gt; &gt;,因为只有通过指针(或 reference_wrappers)才能在相同的容器和 (b) 不构造 N 个默认元素,然后推回实际元素,而是默认构造,然后 .reserve(N) 向量,这样你就有能力在没有重新分配的情况下推到。
  • 那么基本上没有办法在不使用指针的情况下动态分配对象? (我绝对讨厌我不能使用指针,但它超出了我的控制)在这种情况下我唯一的选择是使用堆栈内存分配并拥有预定数量的对象?
  • @Tiisje 如果你使用vector 那么你使用指针。如果您想要任何堆分配,那么您可以使用指针。如果您只想分配堆栈,请考虑将std::pmr 与堆栈分配器一起使用。

标签: c++ abstract-class


【解决方案1】:

你不能这样做

vector<AcctABC> accounts(CLIENTS);

因为它会使CLIENTS 默认构造的抽象基类的数量。您还将lose polymorphism 和诱导object slicing。而是

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

那么例如

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

【讨论】:

    【解决方案2】:

    这里:

    vector<AcctABC> accounts(CLIENTS);
    

    您正在尝试使用CLIENTS 默认构造的AcctABC 元素创建AcctABC 的向量。但是当类是抽象的时,你不能有 AcctABC 元素。在AcctABCs 的向量中也不能有Brass 元素。当您想在向量中存储多态类型时,您需要指针。

    【讨论】:

      猜你喜欢
      • 2013-03-07
      • 1970-01-01
      • 1970-01-01
      • 2013-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-04
      • 1970-01-01
      相关资源
      最近更新 更多