c++虚函数和vtab

虚函数

虚函数主要用来解决基类指针指向派生类时实际指向基类的问题 问题示例

虚析构

虚析构是为了解决基类指针指向派生类时调用指针进行析构不能析构派生类可能导致内存泄漏问题 问题示例

纯虚函数示例

纯虚函数是基类为派生类专门声明的接口,因此派生类如果需要实例化则必须对该纯虚函数进行定义,否则定义不完整不能实例化。

类似纯虚类也是,专门用来提供派生类覆盖模板,派生类必须完全实现才能实例化。

overwrite

  • 用在基类函数后表示该函数必须被派生类进行重写

  • 用在派生类明确表示是对基类的函数覆盖,防止写错函数名或参数表

    此处实验实例化含未实现虚函数的对象实例化会报链接错误,即实例化的类中不能包含未被定义的函数

虚函数原理和vtb

  • 首先区分栈区,代码区,比如new class ,new出的只是类的成员变量部分,而函数在代码区
  • 如果一个类包含虚函数会在new出的对象开始部分增加一个指针大小的空间,用来存储vtab的地址
  • 如果一个类包含虚函数类的大小会增加(64位上前8后4)
  • 含虚函数的类首地址指向虚函数表,而同一个虚函数类同继承的不同实例指向同一个vtab,同一个虚函数的不同继承的实例指向不同vtab
  • 测试代码

虚函数示例代码

#include <iostream>
using namespace std;
class A{
    public:
        void Pt(){
            std::cout << "Pt A" << std::endl;
        }
};
class B:public A{
    public:
        void Pt(){
            std::cout << "Pt B" << std::endl;
        }
};
int main(int argc,char** argv){
    A* oa = dynamic_cast<A*>(new B);
    oa->Pt();//Pt A
    return 0;
}

返回顶部

虚析构示例代码

#include <iostream>
using namespace std;
class A{
    public:
        A(){std::cout << "construct A" << std::endl;}
        ~A(){std::cout << "disconstruct A" << std::endl;}
};
class B:public A{
    public:
        B(){std::cout << "construct B" << std::endl;buf = new char[50];}
        ~B(){std::cout << "disconstruct B" << std::endl;delete buf;}
    private:
        char* buf;
};
int main(int argc,char** argv){
    A* oa = dynamic_cast<A*>(new B);
    delete oa;
    /*construct A
construct B
disconstruct A 
这里看出对象B析构函数未被执行,因此可能会造成内存泄漏*/
    return 0;
}
/*
当两个析构函数都加上virtual前缀后B析构,因此一般在写析构时都会写成虚析构,防止内存泄漏
construct A
construct B
disconstruct B
disconstruct A
*/

返回顶部

vtab示例代码

#include <iostream>
#include <string>
#include <sstream>
#include <iomanip>

std::string to_hex(unsigned char* data, int len) {
    std::stringstream ss;
    ss << std::uppercase << std::hex << std::setfill('0');
    for (int i = 0; i < len; i++) {
        ss << std::setw(2) << static_cast<unsigned>(data[i]);
    }
    return ss.str();
}

class A{
    private: 
    int a;
    public:
    A():a(11){};
    void p();
};
class B{
    protected: 
    int a;
    public:
    B():a(11){};
    virtual void p(){ std::cout << "B Class"<< a << std::endl;};
};
class C:public B{
    public:
    void p(){std::cout << "C Class"<< a << std::endl;};

};
int main()
{
  A a1;
  unsigned char* p1 = reinterpret_cast<unsigned char*>(&a1);
  std::cout << to_hex(p1,sizeof(a1)) << std::endl;//0B000000
  std::cout <<sizeof(a1) << std::endl;//4
  B b1;
  unsigned char* pb1 = reinterpret_cast<unsigned char*>(&b1);
  std::cout << to_hex(pb1,sizeof(b1) ) << std::endl;//40204000000000000B0000009D7F0000
  std::cout <<sizeof(b1) << std::endl;
  C c1;
  unsigned char* pc1 = reinterpret_cast<unsigned char*>(&c1);
  std::cout << to_hex(pc1,sizeof(c1) ) << std::endl;//28204000000000000B0000009D7F0000
  std::cout <<sizeof(b1) << std::endl;
  C c2;
  unsigned char* pc2 = reinterpret_cast<unsigned char*>(&c2);
  std::cout << to_hex(pc2,sizeof(c2) ) << std::endl;//28204000000000000B0000009D7F0000
  std::cout <<sizeof(c2) << std::endl;

  long* vptr_head = (long*)&c2;//取虚函数表首地址
  long rela_func_ptr = *((long*)*vptr_head);
  void (*pf)();
  pf = (void(*)())(rela_func_ptr);
  pf();//未能正确执行,待学习函数指针后再实验
  return 0;
}

返回顶部

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值