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 在继承关系中的体现:在继承关系中,虚函数调用通过虚函数表实现动态绑定,基类指针可以调用派生类的虚函数。
相关推荐
蚊子码农几秒前
每日一题--C语言指针与内存泄漏:一道小问题的深度复盘
c语言·开发语言
Fanfanaas几秒前
Linux 系统编程 进程篇(一)
linux·运维·服务器·c语言·开发语言·网络·学习
星辰徐哥4 分钟前
ARP缓存表:作用、查看方法与刷新技巧
开发语言·缓存·php
ego.iblacat6 分钟前
lvs 集群部署
开发语言·php·lvs
沐雪轻挽萤8 分钟前
6. C++17新特性-编译期 if 语句 (if constexpr)
开发语言·c++
水云桐程序员11 分钟前
C语言编程基础,输入与输出
c语言·开发语言·算法
爱代码的小黄人13 分钟前
MATLAB中for循环实现递减遍历(通用方法)
开发语言·matlab
weixin_7042660517 分钟前
手机体检预约系统开发解析
java·开发语言
白露与泡影18 分钟前
Java八股文大全(2026最新版)大厂面试题附答案详解
java·开发语言
apcipot_rain23 分钟前
【天梯赛】2026天梯赛模拟赛——题解
开发语言·c++·算法·蓝桥杯·天梯赛