C++ 函数语义学——指向成员函数的指针和 vcall

指向成员函数的指针和 vcall

指向成员函数的指针和 vcall

    • [指向成员函数的指针和 vcall](#指向成员函数的指针和 vcall)
      • [1. 不用类对象能否调用类的虚函数和普通函数](#1. 不用类对象能否调用类的虚函数和普通函数)
      • [2. 指向成员函数的指针](#2. 指向成员函数的指针)
      • [3. 指向虚成员函数的指针和 vcall](#3. 指向虚成员函数的指针和 vcall)
      • [4. vcall 在继承关系中的体现](#4. vcall 在继承关系中的体现)
      • 总结

1. 不用类对象能否调用类的虚函数和普通函数

从本质上来讲:只要一个函数有地址,就可以直接去调用(类的普通成员函数包括虚函数都是有独立地址的)。

示例代码
cpp 复制代码
#include <iostream>

// 将一个成员函数的地址转换成普通地址
template<typename dst_type, typename src_type>
dst_type pointer_cast(src_type src)
{
    return *reinterpret_cast<dst_type*>(&src);
}

class TDF
{
public:
    void myf()
    {
        std::cout << "The myf() member function executes" << std::endl;
    }

    void myfWithParam(int val)
    {
        std::cout << "The myfWithParam() member function executes with value: " << val << std::endl;
    }

    virtual void myVirtualFunc()
    {
        std::cout << "The myVirtualFunc() virtual function executes" << std::endl;
    }
};

int main()
{
    // 获取成员函数的地址
    auto memberFuncPtr = &TDF::myf;
    auto memberFuncWithParamPtr = &TDF::myfWithParam;
    auto virtualFuncPtr = &TDF::myVirtualFunc;

    // 打印成员函数的地址
    std::cout << "The address of the TDF::myf function is " << reinterpret_cast<void*>(memberFuncPtr) << std::endl;
    std::cout << "The address of the TDF::myfWithParam function is " << reinterpret_cast<void*>(memberFuncWithParamPtr) << std::endl;
    std::cout << "The address of the TDF::myVirtualFunc function is " << reinterpret_cast<void*>(virtualFuncPtr) << std::endl;

    // 将成员函数指针转换为普通函数指针
    typedef void(*Func)();
    typedef void(*FuncWithParam)(int);
    Func func1 = pointer_cast<Func>(memberFuncPtr);
    FuncWithParam func2 = pointer_cast<FuncWithParam>(memberFuncWithParamPtr);
    Func func3 = pointer_cast<Func>(virtualFuncPtr);

    // 直接调用普通函数指针
    func1();   // 正常调用
    func2(20); // 能调用,但传入的值读取错误 与 pointer_cast<Func>(memberFuncWithParamPtr) 不传入参数一样
    func3();   // 没有被调用,程序异常结束

    return 0;
}
解释
  1. 模板函数 pointer_cast:用于将成员函数指针转换为普通函数指针。
  2. 获取成员函数的地址 :通过 &TDF::myf&TDF::myfWithParam&TDF::myVirtualFunc 获取无参数和有参数的成员函数以及虚函数的地址。
  3. 转换为普通函数指针 :使用 pointer_cast 将成员函数指针转换为普通函数指针。
  4. 直接调用普通函数指针func1func2func3 调用无参数和有参数的成员函数以及虚函数。
注意事项
  • 这种方法依赖于编译器的实现细节,可能在不同的编译器或平台上表现不同。
  • 直接调用成员函数指针可能会导致未定义行为,特别是在涉及虚函数时,因为虚函数调用需要通过虚函数表(vtable)进行动态绑定。

这种方法展示了如何在不用类对象的情况下调用类的虚函数和普通成员函数,但应谨慎使用,确保代码的可移植性和安全性。

2. 指向成员函数的指针

指向成员函数的指针是C++中的一种特殊指针,用于指向类的成员函数。它的语法与普通函数指针不同,需要指定类类型。

  • 定义和使用指向成员函数的指针

    cpp 复制代码
    class MyClass {
    public:
        void memberFunc() {
            std::cout << "Member function called" << std::endl;
        }
    };
    
    int main() {
        MyClass obj;
        void (MyClass::*funcPtr)() = &MyClass::memberFunc;
        (obj.*funcPtr)(); // 通过对象和指针调用成员函数
        return 0;
    }

3. 指向虚成员函数的指针和 vcall

指向虚成员函数的指针与指向普通成员函数的指针类似,但在调用时会通过虚函数表(vtable)进行动态绑定(vcall)。

  • 定义和使用指向虚成员函数的指针

    cpp 复制代码
    class Base {
    public:
        virtual void virtualFunc() {
            std::cout << "Base virtual function called" << std::endl;
        }
    };
    
    class Derived : public Base {
    public:
        void virtualFunc() override {
            std::cout << "Derived virtual function called" << std::endl;
        }
    };
    
    int main() {
        Derived obj;
        void (Base::*funcPtr)() = &Base::virtualFunc;
        (obj.*funcPtr)(); // 通过对象和指针调用虚成员函数,实际调用 Derived::virtualFunc
        return 0;
    }

4. vcall 在继承关系中的体现

在继承关系中,虚函数调用(vcall)通过虚函数表(vtable)实现动态绑定。每个多态类都有一个虚函数表,包含指向虚函数的指针。对象通过虚指针(vptr)指向虚函数表,从而实现动态绑定。

  • 继承关系中的 vcall

    cpp 复制代码
    class Base {
    public:
        virtual void virtualFunc() {
            std::cout << "Base virtual function called" << std::endl;
        }
    };
    
    class Derived : public Base {
    public:
        void virtualFunc() override {
            std::cout << "Derived virtual function called" << std::endl;
        }
    };
    
    void callVirtualFunc(Base* obj) {
        obj->virtualFunc(); // 通过基类指针调用虚函数,实际调用 Derived::virtualFunc
    }
    
    int main() {
        Derived obj;
        callVirtualFunc(&obj); // 传递 Derived 对象的地址
        return 0;
    }

    在上述代码中,callVirtualFunc 函数通过基类指针调用虚函数 virtualFunc,实际调用的是 Derived 类的 virtualFunc,这就是 vcall 在继承关系中的体现。

总结

  • 不用类对象调用函数:直接调用成员函数指针可能会导致未定义行为,特别是在涉及虚函数时,因为虚函数调用需要通过虚函数表(vtable)进行动态绑定。
  • 指向成员函数的指针:指向成员函数的指针用于指向类的成员函数,可以通过对象和指针调用成员函数。
  • 指向虚成员函数的指针和 vcall:指向虚成员函数的指针在调用时通过虚函数表进行动态绑定,实现多态性。
  • vcall 在继承关系中的体现:在继承关系中,虚函数调用通过虚函数表实现动态绑定,基类指针可以调用派生类的虚函数。
相关推荐
fengfuyao9854 分钟前
CH552多功能音量调节旋钮设计与实现
c语言·开发语言
xushichao198910 分钟前
实时数据压缩库
开发语言·c++·算法
minji...12 分钟前
Linux 文件系统 (三) 软连接和硬链接
linux·运维·服务器·c++·算法
liurunlin88814 分钟前
Go环境搭建(vscode调试)
开发语言·vscode·golang
故事和你9130 分钟前
sdut-python-实验四-python序列结构(21-27)
大数据·开发语言·数据结构·python·算法
SuperEugene34 分钟前
TypeScript+Vue 实战:告别 any 滥用,统一接口 / Props / 表单类型,实现类型安全|编码语法规范篇
开发语言·前端·javascript·vue.js·安全·typescript
liuyao_xianhui38 分钟前
优选算法_模拟_提莫攻击_C++
开发语言·c++·算法·动态规划·哈希算法·散列表
.select.1 小时前
c++ 移动赋值/移动构造函数
开发语言·c++
我是鶸1 小时前
secml-malware python library 源码分析及实践
开发语言·python
散峰而望1 小时前
【基础算法】从入门到实战:递归型枚举与回溯剪枝,暴力搜索的初级优化指南
数据结构·c++·后端·算法·机器学习·github·剪枝