C++之为何需要虚析构函数

代码示例展示了C++中基类包含虚析构函数的重要性。即使析构函数在基类中为空,如果派生类有特定的析构行为,虚析构函数确保正确调用。如果不声明为虚,仅基类的析构函数会被调用,可能导致派生类资源未正确释放。在main函数中创建并管理多个对象实例,强调了动态类型安全销毁的重要性。
摘要由CSDN通过智能技术生成

1.程序代码

// brass.h
#ifndef BRASS_H_
#define BRASS_H_
#include<string>

class Brass{
    private:
       std::string fullName; // 姓名
       long acctNum; // 账户号
       double balance; // 余额
    
    public:
       Brass(const std::string & s = "Nullbody", long an = -1,double bal = 0.0);
       void Deposit(double amt);
       virtual void Withdraw(double amt);
       double Balance() const;
       virtual void ViewAcct() const;
       virtual ~Brass(){}
};

class BrassPlus : public Brass{
   private:
      double maxLoan;// 透支额度
      double rate; // 透支额度收取的利率
      double owesBank; // 欠银行的钱
    public:
      BrassPlus(const std::string & s = "Nullbody",long an = -1,double bal = 0.0,double ml = 500.0,double r = 0.11125);
      BrassPlus(const Brass & ba,double ml = 500,double r = 0.11125);
      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

// brass.cpp
#include<iostream>
#include"brass.h"
using std::cout;
using std::endl;
using std::string;
typedef std::ios_base::fmtflags format;
typedef std::streamsize precis;
void restore(format f,precis);
format setFormat();
Brass::Brass(const string & s,long an,double bal):fullName(s),acctNum(an),balance(bal){

}

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

void Brass::Withdraw(double amt){
    format initialState = setFormat();
    precis prec = cout.precision(2);
    if(amt < 0){
        cout << "Withdrawal amount must be positive;" << "withdrawal canceled.\n";

    }else if( amt <= balance){
        balance -= amt;
    }else{
        cout << "Withdrawal amount of $" << amt << " exceeds your banlace.\n" << "Withdrawal canceled.\n";
    }
    restore(initialState,prec);
}
double Brass::Balance() const{
    return balance;
}
void Brass::ViewAcct() const{
    format initialState = setFormat();
    precis prec = cout.precision(2);
    cout << "Client: " << fullName << endl;
    cout << "Account Number: " << acctNum << endl;
    cout << "Balance : $" << balance << endl;
    restore(initialState,prec);
}
BrassPlus::BrassPlus(const string & s,long an,double bal,double ml,double r):Brass(s,an,bal){
    maxLoan = ml;
    owesBank = 0.0;
    rate = r;
}

BrassPlus::BrassPlus(const Brass & ba,double ml,double r):Brass(ba){
    maxLoan = ml;
    owesBank = 0.0;
    rate = r;
}

void BrassPlus::ViewAcct()const{
    format initialState = setFormat();
    precis prec = cout.precision(2);
    Brass::ViewAcct();
    cout << "Maximum loan: $" << maxLoan << endl;
    cout << "Owed to bank: $" << owesBank << endl;
    cout.precision(3);
    cout << "Loan Rate: " << 100 * rate << "% \n";
    restore(initialState,prec);
}
void BrassPlus::Withdraw(double amt){
    format initialState = setFormat();
    precis prec = cout.precision(2);
    double bal = Balance();
    if(amt <= bal){
        Brass::Withdraw(amt);
    }else if(amt <= bal + maxLoan - owesBank){
        double advance = amt - bal;
        owesBank += advance * (1.0 + rate);
        cout << "Bank advance : $ " << advance << endl;
        cout << "Finance charge: $" << advance * rate << endl;
        Deposit(advance);
        Brass::Withdraw(amt);
    }else{
        cout << "Credit limit exceeded. Transaction cancelled.\n";
    }
    restore(initialState,prec);

}

format setFormat(){
    return cout.setf(std::ios_base::fixed,std::ios_base::floatfield);
}
void restore(format f,precis p){
    cout.setf(f,std::ios_base::floatfield);
    cout.precision(p);
}

// main.cpp

#include <iostream>
#include"brass.cpp"
#include<string>
const int CLIENTS = 4;
int main(int, char**) {
   using std::cin;
   using std::cout;
   using std::endl;
   Brass * p_clients[CLIENTS];
   std::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 or " << "2 for BrassPlus Account: ";
      while(cin>>kind &&(kind !='1' && kind != '2')){
         cout << "Enter either 1 or 2: ";
      }
      if(kind == '1'){
         p_clients[i] = new 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;
         p_clients[i] = new BrassPlus(temp,tempnum,tempbal,tmax,trate);
      }
      while(cin.get() != '\n'){
         continue;
      }
   }
   cout << endl;
   for(int i = 0; i < CLIENTS; i++){
      p_clients[i]->ViewAcct();
      cout << endl;
   }
   for(int i = 0; i < CLIENTS; i++){
      delete p_clients[i];
   }
   cout << "Done. \n";

   return 0;
}

       在上述程序代码中使用delete 释放由 new 分配的对象的代码说明了为何基类应包含一个虚析构函数,虽然有时好像并不需要析构函数。如果析构函数不是虚的,则将只调用对应于指针类型的析构函数。对于上述程序代码,这意味着只有 Brass的析构函数被调用,即使指针指向的是一个 BrassPlus 对象。如果析构函数是虚的,将调用相应对象类型的析构函数。因此,如果指针指向的是 BrassPlus 对象,将调用 BrassPlus的析构函数,然后自动调用基类的析构函数。因此,使用虚析构函数可以确保正确的析构函数序列被调用。对于上述程序代码,这种正确的行为并不是很重要,因为析构函数没有执行任何操作。然而,如果 BrassPlus包含一个执行某些操作的析构函数,则 Brass 必须有一个析构函数,即使该析构函数不执行任何操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值