C++进阶--virtual

目录

一.虚函数

二.虚基类

三.纯虚函数


virtual 在C++中用于三个部分:虚函数、虚基类、纯虚函数。

一.虚函数

一般会在基类中,将派生类会重新定义的函数方法,声明为虚方法

且在派生类中,该虚方法会自动成为虚方法;

在前面类继承中,多态共有继承 brass 示例如下:

// brass.h  -- bank account classes
#ifndef BRASS_H_
#define BRASS_H_
#include <string>
// Brass Account Class
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() {}
};

//Brass Plus Account Class
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,
            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

(1)虚函数

//虚函数
virtual void Withdraw(double amt);

//虚函数
virtual void ViewAcct() const;

可以看到,在派生类同样也声明了这两个虚函数,这说明同一个函数有不同的行为,不同行为取决于具体对象,如下所示:

Brass dom;
BrassPlus dot;

dom.ViewAcct();    //use Brass::ViewAcct()
dot.ViewAcct();    //use BrassPlus::ViewAcct()

如果方法是通过引用、指针,而不是对象调用的:

  • 如果没有关键字 virtual ,程序将根据引用、指针类型选择方法;
  • 如果有关键字 virtual ,程序将根据引用、指针指向的对象类型选择方法;
Brass dom;
BrassPlus dot;

Brass & b1_ref = dom;
Brass & b2_ref = dot;

#  if unuse virtual
b1_ref.ViewAcct();    //use Brass::ViewAcct()
b2_ref.ViewAcct();    //use Brass::ViewAcct()

#  if use virtual
b1_ref.ViewAcct();    //use Brass::ViewAcct()
b2_ref.ViewAcct();    //use BrassPlus::ViewAcct()

(2)虚析构函数

//虚析构函数
virtual ~Brass() {}

如果方法是通过引用、指针,而不是对象调用的:

  • 如果没有关键字 virtual ,程序将根据引用、指针类型选择析构函数;
  • 如果有关键字 virtual ,程序将根据引用、指针指向的对象类型选择析构函数;

比如前面继承的例子:

// usebrass2.cpp -- polymorphic example
// compile with brass.cpp
#include <iostream>
#include <string>
#include "brass.h"
const int CLIENTS = 4;

int main()
{
   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];  // free memory
   }
   cout << "Done.\n";         
 /* code to keep window open 
   if (!cin)
      cin.clear();
   while (cin.get() != '\n')
      continue;
*/
   return 0; 
}

通过 new 创建对象后,当通过 delete 释放时:

  • 如果不是虚析构函数,因为指针 p_clients 的类型是 Brass 类,所以这里只会调用 Brass 类的析构函数;
  • 如果是虚析构函数,即使指针 p_clients 的类型是 Brass 类,这里也会调用 相应对象的析构函数;

二.虚基类

虚基类也叫抽象基类(ABC),该基类包含多个派生类的共性;

一般在虚函数中,当至少有一个纯虚函数时,这个类就叫 虚基类/抽象基类;

抽象基类不能创建该类的对象,必须是其派生类才能创建对象;

三.纯虚函数

纯虚函数是指在虚函数尾部加上 = 0,用于提供为实现的函数:

    virtual void ViewAcct() const = 0;     // pure virtual function

包含纯虚函数的类只能作为基类,且不能创建该类的对象;

示例:

// acctabc.h  -- bank account classes
#ifndef ACCTABC_H_
#define ACCTABC_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

说明:

  • 可以看到程序声明了一个基类 AcctABC ,因为该基类包含了两个纯虚函数,所以该基类也叫虚基类/抽象基类。
  • 两个纯虚函数:
virtual void Withdraw(double amt) = 0; // pure virtual function
virtual void ViewAcct() const = 0;     // pure virtual function

是两个派生类(Bras、BrassPlus)公有的方法,只是方法实现各有不同,所以在派生类中要对这两种方法重新定义,在虚基类中不用定义;

  • 对于虚基类的其他方法,派生类可以正常使用;

在继承中,ABC常被看作一种必须实施的接口。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值