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

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

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

3. 如果子类重写了某个虚函数,则该类的虚函数表对应位置上的虚函数地址会被覆盖。如果完全不重写,则子类的虚函数表里面的内容和父类虚函数表内容保持一致。(需要注意的是,虽然父类的函数地址被覆盖了,但是默认的参数还是保留了,如果用父类指针去调用子类重载的函数,传的默认参数值还是父类给的默认值,只有用子类本身类型的指针去调用,才会传子类给的默认值)。

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

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

6. 多重继承(有多个父类并列)时,该类有多张虚函数表,实例对象的0~3字节,表示第一个虚函数表地址,4~n字节表示第一个父类的成员变量;n+1~n+4表示第二张虚函数表地址,n+5~m表示第二个父类成员变量,以此类推(C++对象内存模型)。当这些父类又有共同祖先时,为避免数据冗余和冲突,继承方式应该是虚继承。

7. 用父类指针指向子类对象时,指针地址会根据多重继承顺序的发生相应的偏移。如A:B,C表示A同时继承了B和C,当用B*类型指向A类对象时,指针偏移0字节,当用C类型指向A类对象时,指针偏移sizeof(B)字节。用代码表示如下:

#ifdef __WIN64

typedef __int64 ADTY;

#else

typedef __int32 ADTY;

#endif

A obj;

A* a_ptr = &obj;

B* b_ptr = &obj;

C* c_ptr = &obj;

assert(((ADTY)c_ptr - (ADTY)b_ptr) == sizeof(B));

assert((ADTY)a_ptr == (ADTY)b_ptr);

8. 在构造一个对象时,对象的虚表指针指向的地址会随着构造过程发生变化。执行父类构造函数时,虚表指针指向了父类的虚表,执行子类构造函数时,虚表指针指向子类虚表。因此在构造函数中调用虚函数不会发生多态效应。

9. 析构函数不会被子类继承,在delete多态指针时,如果发现指针类型的析构函数为虚,编译器将会动用一系列复杂的机制,找到该指针真实的类型,进而调用真实的析构(该过程是静态绑定,不是运行时动态调整的)。因此,如果一个类可能会被继承,则必须将其析构函数定义为virtual。

10. 即便父类的虚函数是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() {}
};

class BrassExtra
{
public:
    int vvk;
    BrassExtra() :vvk(97) {}
    virtual void SecondMethod()
    {
        cout << "BrassExtra SecondMethod" << endl;
    }
};

//即便子类不再将父类的虚函数函数声明为virtual,只要祖先类中该函数是virtual,则它依然是虚函数
class BrassPlus :public Brass,public BrassExtra {
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 SecondMethod();
    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) { cout << "fullname: " << fullname <<","<<"acctNum: "<< acctNum<<","<<"balance: "<< balance << endl; }
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 BrassPlus::SecondMethod() { cout << "BrassPlus SecondMethod: " << 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 << *(string*)((ADTY*)bas[1] + 1) << endl;//Brass::fullname
    cout << *(long*)(((char*)((ADTY*)bas[1] + 1) + sizeof(string)) + 0) << endl;//Brass::acctNum
    cout << *(double*)(((char*)((ADTY*)bas[1] + 1) + sizeof(string)) + sizeof(long) + 4) << endl;//Brass::balance
    cout << "对象bas[1]的第二个虚函数表位于:" << hex << *(ADTY*)(((char*)((ADTY*)bas[1] + 1) + sizeof(string)) + sizeof(long) + 4+sizeof(double)) << endl;//+4表示内存对齐,与结构体对齐规则一致
    cout <<  *(int*)(((char*)((ADTY*)bas[1] + 1) + sizeof(string)) + sizeof(long) + 4 + sizeof(double)+ sizeof(ADTY)) << endl;//BrassExtra::vvk

    cout << "子类虚函数表会从父类继承(复制一份,不是共用一份),如果子类重写了某个虚函数,则子类的虚函数表中对应的函数地址会被覆盖" << endl;

    Fun bvvv = (Fun)(*((ADTY*)*(ADTY*)(((char*)((ADTY*)bas[1] + 1) + sizeof(string)) + sizeof(long) + 4 + sizeof(double)) + 0));//对象bas[1]的第二个虚函表的第一个虚函数
    bvvv();
    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;
}

 

 

 

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值