• C++虚函数表理解


    一,思维模式图

    二,代码验证

    class A {
    public:
        A(int x) {
            fProtected = x;
        }
        float GetFProtected() {
            return fProtected;
        }
    
    public:
        float fpublic = 2.3f; //c++11支持了初始化,但不能使用auto
        string sname = "liqi";
        CMyNumOperator<int>* on = new CMyNumOperator<int>(); //对象也可以
    
        void TestFunc() {
            cout << "TestFunc" << endl;
        }
    
        static void StaticTestFunc() {
            cout << "Static-TestFunc" << endl;
        }
        virtual void ToString() {
            cout << "A::ToString" << endl;
        }
    protected:
        float fProtected;
        void ProtectedFunc() {
            cout << "PRotectedFunc" << endl;
        }
    private:
        void PrivateFunc() {
            cout << "PrivateFunc" << endl;
    
        }
    
    };
    
    //只管公有继承,不管保护继承和私有继承,意义不大,也太复杂
    class B : public A {
    public:
        friend void TestProtectedDerive();
        B() :A(1) {}
        void TestForDerive() {
            //公有继承下
            //1,子类可以访问父类的保护成员,不能访问父类的私有成员
            B ob;
            //PrivateFunc(); //error,子类不能访问基类的私有成员
            ProtectedFunc(); //right
            fProtected = 10; //right
            ob.fProtected = 20; //right
        }
    
        //1,c++中只要基类有相同签名虚函数,则默认为此基类函数也是虚函数[与C#不同],如下情形都成立
        // (1) 函数不声明 virtual
        // (2) 函数声明了 virtual
        // (3) 函数声明了 override
        // (4) 函数声明了 virtual 和 override
        //2,c++中两个关键词作用不同,可以同时存在
        // virtual仅表明函数是虚函数,override是C++11中出现的,明确说明是对基类的重写
        // 它的好处是当函数声明不符合规则时,编译器会报错
        void virtual ToString() override{
            cout << "B::ToString" << endl;
        }
    };
    
    void TestVirtualFunctionTable() {
        cout << hex;
         typedef void(*PFUNC)();
    
        offsetof(A, fpublic); //利用此函数可以算函数布局
    
        A oa(0);
        B ob;
    
        //一,通过内存地址修改不可访问的保护变量
        *(float*)((int*)&oa + 1) = 123.4f; //类的第一个变量fpublic赋值,(int*)&oa + 1是跳过虚函数指针
        float fpublic = oa.fpublic;
    
        //二,通过内存地址调用虚函数
        //A和B的虚函数表地址不一样,也就是说父类和子类各有一张虚函数表
        int* pvptr = (int*)(*((int*)(&oa)));
        cout << "A的虚函数表地址:" << pvptr << endl;    //000DB0D4
        ((void(*)())(*pvptr))();                    //A::ToString
        
        pvptr = (int*)(*((int*)(&ob)));
        cout << "B的虚函数表地址:" << pvptr << endl; //000DB128
        ((void(*)())(*pvptr))();                    //B::ToString
    
    
        cout << "--------------------------" << endl;
        //最简写法
        ((void(*)())(*((int*)*(int*)&oa)))();
        ((void(*)())(*((int*)*(int*)&ob)))();
    
    }
  • 相关阅读:
    Linux 管道 管线
    中国网络拓扑图(主干网、地区网、主节点一览表)
    C++中的STL中map用法详解
    Git 丢弃本地修改
    Linux下指定so动态库的加载路径的5种方法!!!
    C++中string append函数的使用与字符串拼接
    C++执行shell命令linux
    Linux动态库.so文件加载搜索路径详解
    FFmpeg打印日志函数分析
    linux C/C++中调用shell命令和运行shell脚本
  • 原文地址:https://www.cnblogs.com/timeObjserver/p/9337813.html
Copyright © 2020-2023  润新知