📘 C++ 中成员函数与普通函数的地址是否唯一?

📘 C++ 中成员函数与普通函数的地址是否唯一?

在 C++ 编程中,我们经常会使用函数指针来引用普通函数或类的成员函数。表面上看,它们都像是函数的地址,但实际上它们在底层的行为和内存模型是截然不同的。本篇文章将深入讲解这两者的区别,特别是函数地址是否唯一这个问题。


🔹 一、普通函数的地址是唯一的吗?

✅ 答案:是的,普通函数的地址是唯一的

普通函数在编译时会被编译器放入代码段(.text 段) ,所有地方使用的都是这段代码的唯一地址。

示例:

c 复制代码
#include <iostream>

void hello() {
    std::cout << "Hello!" << std::endl;
}

int main() {
    void (*fp1)() = &hello;
    void (*fp2)() = &hello;

    std::cout << "fp1 = " << (void*)fp1 << std::endl;
    std::cout << "fp2 = " << (void*)fp2 << std::endl;
}

输出示例:

ini 复制代码
fp1 = 0x4005d6
fp2 = 0x4005d6

✅ 两个函数指针地址一致,说明函数地址是唯一的。


🔹 二、成员函数的地址是唯一的吗?

✅ 答案:成员函数代码是唯一的,但指针形式可能更复杂,不总是简单地址。

解释:

  • 成员函数的实现(代码)在内存中只有一份,也位于代码段。
  • 成员函数指针 (如 void (Class::*)())并不直接表示函数地址,而是一个结构体或偏移量。
  • 它依赖于当前对象的 this 指针来访问属性。
  • 编译器在调用时会自动传入 this

🔸 举例说明:

csharp 复制代码
class Person {
public:
    int age;

    void setAge(int a) {
        age = a; // 实际是 this->age = a
    }
};

int main() {
    void (Person::*mptr)(int) = &Person::setAge;

    Person p1, p2;
    (p1.*mptr)(10);  // p1.age = 10
    (p2.*mptr)(20);  // p2.age = 20
}

内部原理:

arduino 复制代码
// 编译器会翻译为:
Person::setAge(&p1, 10);  // this = &p1

函数里访问 age 实际上是:

ini 复制代码
this->age = a;

🔹 三、成员函数指针 vs 普通函数指针

类型 是否唯一 内部结构 访问依赖 调用方式
普通函数指针 ✅ 唯一地址 简单地址 fp()(*fp)()
成员函数指针(非虚) ✅ 函数唯一 指针可能结构化 函数地址 + this偏移(可能) 需要对象 (obj.*fptr)()
成员函数指针(虚函数、多继承) ❌ 结构更复杂 包含虚表偏移、this调整等 需要对象 + 虚表 (obj.*fptr)()

🔹 四、为什么成员函数能访问对象属性?

✅ 关键在于:this 指针

  • 每次调用成员函数,编译器自动传入对象的地址(即 this)。
  • 所以虽然函数代码是共享的,但它总是通过 this-> 来访问当前对象的成员变量

类比说明:

成员函数像是一把剪刀,剪刀是共享的(函数唯一),但你每次使用它剪不同的布(不同对象),你告诉它剪哪块布(this),结果就不同。


🔹 五、内存模型对比(示意)

假设类如下:

csharp 复制代码
class MyClass {
public:
    int x;
    void foo();
};

创建两个对象:

css 复制代码
MyClass a, b;

内存示意:

lua 复制代码
堆或栈:
+---------+       +---------+
| a 对象  | ----> | x: 10   |
+---------+       +---------+
| b 对象  | ----> | x: 20   |
+---------+       +---------+

代码段:
+------------------+
| MyClass::foo()   | ← 所有对象共享这段代码
+------------------+

✅ 总结一句话:

普通函数的指针是全局唯一的地址;成员函数的代码是唯一的,但它通过 this 指针访问不同对象的成员变量,成员函数指针本身可能有额外信息来支持多态或继承。

相关推荐
John_ToDebug1 小时前
浏览器冷启动与热启动机制全解析:原理、案例与性能优化实战
c++·chrome·性能优化
啊阿狸不会拉杆1 小时前
《算法导论》第 7 章 - 快速排序
开发语言·数据结构·c++·算法·排序算法
Dream it possible!2 小时前
LeetCode 面试经典 150_数组/字符串_O(1)时间插入、删除和获取随机元素(12_380_C++_中等)(哈希表)
c++·leetcode·面试·哈希表
kebeiovo4 小时前
C++实现线程池(3)缓存线程池
开发语言·c++
半桔5 小时前
【STL源码剖析】从源码看 vector:底层扩容逻辑与内存复用机制
java·开发语言·c++·容器·stl
千里镜宵烛6 小时前
互斥锁与条件变量
linux·开发语言·c++·算法·系统架构
爱科研的瞌睡虫6 小时前
C++线程中 detach() 和 join() 的区别
java·c++·算法
凤年徐6 小时前
【数据结构与算法】刷题篇——环形链表的约瑟夫问题
c语言·数据结构·c++·算法·链表
牟同學7 小时前
深入理解 C++ 中的stdpriority_queue:从原理到实战的高效优先级管理
数据结构·c++·priority_queue