c++ 继承关系中的虚函数表

  1. 子类继承父类的虚函数表的方式是复制一份。存在虚函数的类,都有自己的虚函数表,不与其他类共用。

  2. 只要祖先类的某个函数被声明位virtual, 则在后代中无论是否显式地添加virtual,该函数一直都是虚的。

  3. 如果子类重写了某个虚函数,则该类的虚函数表对应位置上的虚函数地址会被覆盖。如果完全不重写,则子类的虚函数表里面的内容和父类虚函数表内容保持一致。

  4. 虚函数表类似于类的静态成员,被该类的所有实例共享。只不过该表是只读的,不存在线程安全问题。

  5. 实例对象在起始4字节中,会存放虚函数表的地址(跟系统寻址范围有关,有的系统8字节才能表示一个地址),可以通过手动寻址的方式完成虚函数调用。

  6. 多重继承(有多个父类并列)时,该类有多张虚函数表,03字节,47字节分别表示第一张虚函数表地址,第二张虚函数表地址,以此类推。

  7. 即便父类的虚函数是private的,子类的虚函数表中也依然存在它的地址,我们可以通过子类实例的首地址,寻找到虚函数表进而找到该虚函数,完成调用。这样会违背c++语言的设计初衷,破坏数据的安全性,一般不要轻易使用。

#include <iostream>
#include <string>
using namespace std;
 
typedef void(__stdcall *Fun)();
typedef void(__stdcall *Fun1)(double);
 
//声明virtual,让该成员函数地址在虚函数表中留下记录
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 ViewAcct() const;
    virtual void withdraw(double amt);
    double Balance() const;
    virtual void test(){cout<<"Brass::test"<<endl;}
    virtual ~Brass(){}
};
 
//即便子类不再将父类的虚函数函数声明为virtual,只要祖先类中该函数是virtual,则它依然是虚函数
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);
    void ViewAcct()const;
    void withdraw(double amt);
    void ResetMax(double m){maxLoan=m;}
    void ResetRate(double r){rate = r;}
    void ResetOwes(){owesBank=0;}
};
 
class BrassPlusPlus:public BrassPlus{
public:
    void ViewAcct() const override{cout<<"BrassPlusPlus viewacct"<<endl;}
};
 
 
Brass::Brass(const std::string & s,long an,double bal):fullname(s),acctNum(an),balance(bal){}
void Brass::Deposit(double amt){return;}
void Brass::withdraw(double amt){cout<<"Brass withdraw: "<<amt<<endl;return;}
double Brass::Balance() const{return balance;}
void Brass::ViewAcct()const{cout<<"Brass viewacct"<<endl;}
 
BrassPlus::BrassPlus(const std::string & s,long an,double bal,double ml,double r):Brass(s,an,bal),maxLoan(ml),rate(r){}
BrassPlus::BrassPlus(const Brass &ba,double ml,double r):Brass(ba),maxLoan(ml),rate(r){}
void BrassPlus::ViewAcct()const{cout<<"BrassPlus viewacct"<<endl;}
void BrassPlus::withdraw(double amt){cout<<"BrassPlus withdraw: "<<amt<<endl;return;}
 
void test(const Brass& br){
    br.ViewAcct();
    //br.withdraw(6.0);//编译不通,const 实例只能调用const成员函数;
    Brass& bbp=const_cast<Brass&>(br);
    bbp.withdraw(6.0);
}
 
int main()
{
#ifdef _WIN64
    typedef __int64 ADTY;//64位操作系统,8个字节表示一个地址
#else
    typedef __int32 ADTY;
#endif
    cout << "Hello World!" << endl;
    Brass bra("Jim",2,0.06);
    BrassPlus brap("Jhon",5,0.58,500,0.014);
 
    const Brass bra_2("Jack",3,0.02);
    Brass& b1 = bra;
    Brass& b2 = brap;
 
    Brass *bas[2];
    bas[0] = &bra;
    bas[1] = &brap;
 
    cout << "对象bas[0]的虚函数表位于:"<<hex<<*(ADTY*)bas[0]<<",对象bas[0]的第一个虚函数位于:"<<hex<<*( (ADTY *)*(ADTY*)bas[0] + 0 )<<endl;
    cout << "对象bas[1]的虚函数表位于:" <<hex<<*(ADTY*)bas[1]<<",对象bas[1]的第一个虚函数位于:"<<hex<<*( (ADTY *)*(ADTY*)bas[1] + 0 )<<endl;
    cout << "子类虚函数表会从父类继承(复制一份,不是共用一份),如果子类重写了某个虚函数,则子类的虚函数表中对应的函数地址会被覆盖"<<endl;
 
    Fun cc = (Fun)( *( (ADTY *)*(ADTY*)bas[1] + 0 ) );
    Fun1 dd = (Fun1)( *( (ADTY *)*(ADTY*)bas[1] + 1 ) );
    cout<<"对象bas[1]虚函数调用结果:"<<endl;
    cc();
    dd(8.9);
 
    ADTY *ptr = (ADTY*)(&bra);
 
    //可以看出虚函数表类似于类的静态成员,对象的首地址位置保存了虚函数表的地址
    cout << "对象bra的虚函数表位于:"<<hex<<*ptr<<",对象bra的第一个虚函数位于:"<<hex<<*( (ADTY*)*ptr + 0 )<<endl;
    cout << "对象bra_2的虚函数表位于:" <<hex<<*(ADTY*)(&bra_2)<<",对象bra_2的第一个虚函数位于:"<<hex<<*( (ADTY*)*(ADTY*)(&bra_2) + 0 )<<endl;
    cout << "说明bra的虚函数表 与 bra_2的虚函数表 是同一张表"<<endl;
 
    ADTY* ptr1 =(ADTY*)*ptr;
    Fun pFun = (Fun)*(ptr1+0); //等效于 (Fun)(*((ADTY*)*(ADTY*)(&bra)+0));
    pFun();
 
    Fun1 pFun1 = (Fun1)*(ptr1+1); //等效于 (Fun)(*((ADTY*)*(ADTY*)(&bra)+1));
    pFun1(9.0);
    cout << "Hello World end" << endl;
    return 0;
}
posted @ 2022-07-18 11:47  萧海~  阅读(49)  评论(0编辑  收藏  举报