找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2344

积分

0

好友

342

主题
发表于 昨天 08:16 | 查看: 8| 回复: 0

C++ 中获取对象地址的两种方式:std::addressof 函数和 & 取地址运算符,它们的区别和适用场景是什么?我会从基础到进阶帮你梳理清楚。

一、基础概念:& 运算符的常规用法

& 是 C++ 原生的取地址运算符。在绝大多数场景下,它能正确返回对象或变量在内存中的实际地址,也是最常用的取地址方式。

1. 基本使用示例

#include <iostream>
using namespace std;
int main() {
    // 普通变量取地址
    int a = 10;
    int* p_a = &a;
    cout << "变量a的地址: " << p_a << endl; // 输出a的内存地址
    // 类对象取地址
    class MyClass {
    public:
        int x = 5;
    };
    MyClass obj;
    MyClass* p_obj = &obj;
    cout << "对象obj的地址: " << p_obj << endl; // 输出obj的内存地址
    return 0;
}

说明:

  • 对于普通变量、未重载 operator& 的类对象,& 直接返回其真实内存地址。
  • 语法简单,性能无额外开销,是日常开发的首选。

2. & 运算符的 “陷阱”:重载导致的失效

& 运算符可以被类重载,这会导致它不再返回对象的真实地址,而是返回重载函数指定的内容 —— 这是 & 最关键的局限性。

示例:重载 operator& 后的异常情况

#include <iostream>
using namespace std;
class BadClass {
public:
    // 重载取地址运算符,返回固定值(非真实地址)
    void* operator&() {
        return (void*)0x12345678; // 伪造地址
    }
    // 甚至可以重载const版本
    const void* operator&() const {
        return (void*)0x87654321;
    }
    int val = 99;
};
int main() {
    BadClass obj;
    // 调用重载的operator&,返回伪造的0x12345678,而非obj的真实地址
    cout << "重载&后的地址: " << &obj << endl;
    // 尝试直接访问成员,地址是真实的(对比用)
    cout << "obj.val的真实地址: " << &obj.val << endl;
    return 0;
}

输出:

重载&后的地址: 0x12345678
obj.val的真实地址: 0x7ffeeabc08ac

可以看到:&obj 被重载函数篡改,返回了虚假地址,而非对象的真实内存地址。

二、std::addressof:获取真实地址

std::addressof 是 C++11 引入的标准库函数(定义在 <memory> 头文件)。它的核心作用是无视 operator& 的重载,强制返回对象的真实内存地址

1. 基本使用示例

#include <iostream>
#include <memory> // 必须包含此头文件
using namespace std;
class BadClass {
public:
    void* operator&() {
        return (void*)0x12345678; // 重载&,返回伪造地址
    }
    int val = 99;
};
int main() {
    BadClass obj;

    // 用&取地址:返回重载后的伪造地址
    cout << "&obj: " << &obj << endl;
    // 用std::addressof取地址:返回真实地址
    cout << "addressof(obj): " << addressof(obj) << endl;
    // 验证:真实地址和成员val的地址前缀一致
    cout << "&obj.val: " << &obj.val << endl;
    return 0;
}

输出:

&obj: 0x12345678
addressof(obj): 0x7ffeeabc08a8
&obj.val: 0x7ffeeabc08ac

可以看到:std::addressof(obj) 成功绕过了重载的 operator&,返回了对象的真实地址(和 obj.val 的地址属于同一内存区域)。

2. std::addressof 的底层原理(简化理解)

std::addressof 之所以能无视重载,是因为它不调用 operator&,而是通过编译器内置的机制直接获取对象的内存地址。其核心逻辑可简化理解为:

template <typename T>
T* addressof(T& obj) noexcept {
    // 通过指针操作绕过operator&重载,直接取真实地址
    return reinterpret_cast<T*>(
        &const_cast<char&>(reinterpret_cast<const volatile char&>(obj))
    );
}

简单说:它通过巧妙的类型转换绕开了用户自定义的 operator&,直接访问对象的原始内存地址。这对于编写可靠的通用库代码至关重要。

三、&std::addressof 的对比与适用场景

特性 & 运算符 std::addressof
语法 简洁,原生支持 需要包含 <memory>,语法稍繁琐
性能 无额外开销(编译期直接解析) 几乎无开销(模板函数,编译器会内联)
重载影响 会被 operator& 重载篡改结果 无视 operator& 重载,返回真实地址
适用场景 普通变量、未重载 operator& 的对象 重载了 operator& 的对象、泛型编程

适用场景说明:

  1. 日常开发(无重载 operator&): 优先用 &,语法简洁,无学习成本。
  2. 泛型编程(模板): 推荐用 std::addressof —— 你无法保证模板参数类型是否重载了 operator&,用 std::addressof 能避免意外错误。
    示例(泛型函数取地址):
    template <typename T>
    void printRealAddress(T& obj) {
        // 无论T是否重载operator&,都能获取真实地址
        cout << "真实地址: " << addressof(obj) << endl;
    }
  3. 需要绝对真实地址的场景: 比如底层内存管理、调试、序列化等,必须确保地址是对象的实际内存位置时,使用 std::addressof

四、std::addressof 的注意事项

  1. 仅支持左值: std::addressof 只能接收左值引用(T&),无法直接获取右值的地址(右值本身无稳定内存地址)。错误示例:std::addressof(10);(编译报错)。
  2. C++11 及以上支持: 如果你的项目需要兼容 C++03,无法使用 std::addressof,需自行规避重载 operator& 的类。
  3. 对函数/数组的处理:& 一致,std::addressof 对函数或数组取地址时,会返回其首地址(函数名/数组名本身就是地址标识)。

总结

  1. & 运算符: C++ 原生取地址方式,简洁高效。但如果类重载了 operator&,它会返回重载后的结果(可能非真实地址)。
  2. std::addressof C++11 引入的标准库函数(需包含 <memory>),核心作用是无视 operator& 重载,强制返回对象的真实内存地址。
  3. 使用原则: 日常场景用 &,泛型编程或需要获取绝对真实地址的场景用 std::addressof,这样可以有效避免因重载 operator& 而导致的意外错误,写出更健壮的代码。

如果你想了解更多关于 C++ 底层机制和现代特性的深入讨论,欢迎访问 云栈社区




上一篇:2025前端框架热度榜单发布:Vue跌至第五,新秀Ripple表现亮眼
下一篇:Stack Overflow的兴衰史:开发者圣地如何被AI编程工具冲击
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-11 14:18 , Processed in 0.291755 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表