cpp自学 day2(—>运算符)

cpp 复制代码
#include <iostream> // 用于输入输出,如 std::cout, std::endl
#include <string>   // 图片中包含此头文件,尽管在此示例中未使用

// 定义 Entity 类,这是 ScopedPtr 将要管理的类型
class Entity {
public:
    int x; // Entity 类的一个公共成员变量

    // Entity 类的默认构造函数
    Entity() : x(0) { // 初始化 x 为 0
        std::cout << "Entity 构造" << std::endl;
    }

    // Entity 类的析构函数
    ~Entity() {
        std::cout << "Entity 析构" << std::endl;
    }

    // Entity 类的一个公共成员函数
    void Print() const { // const 表示此函数不会修改对象的状态
        std::cout << "Hello! x = " << x << std::endl;
    }
};

// 定义 ScopedPtr 类,一个简单的智能指针模拟
class ScopedPtr {
private:
    Entity* m_Obj; // 内部封装一个裸指针,指向 Entity 对象

public:
    // 构造函数:接受一个 Entity* 裸指针,接管其所有权
    ScopedPtr(Entity* entity)
        : m_Obj(entity) // 使用成员初始化列表初始化 m_Obj
    {
        // 构造函数体为空,因为初始化已在成员初始化列表中完成
        std::cout << "ScopedPtr 构造,管理地址: " << m_Obj << std::endl;
    }

    // 析构函数:当 ScopedPtr 对象销毁时,自动释放其管理的 Entity 对象
    ~ScopedPtr() {
        if (m_Obj) { // 确保 m_Obj 不是空指针,避免对空指针进行 delete
            std::cout << "ScopedPtr 析构,释放地址: " << m_Obj << std::endl;
            delete m_Obj; // 释放 m_Obj 指向的内存
            m_Obj = nullptr; // 将指针置空,防止悬空指针
        }
    }

    // 重载 operator->() (箭头运算符)
    // 作用:允许通过 ScopedPtr 对象像访问指针一样访问 Entity 对象的成员
    // 返回值:通常返回内部管理的裸指针
    Entity* operator->() {
        return m_Obj; // 返回内部存储的 Entity* 裸指针
    }

    // 重载 GetObject() 方法,返回内部裸指针
    Entity* GetObject() { return m_Obj; }

    // 禁用复制构造函数和复制赋值运算符,以实现独占所有权(类似 std::unique_ptr)
    // 防止多个 ScopedPtr 共同管理同一块内存,导致重复释放。
    ScopedPtr(const ScopedPtr&) = delete;
    ScopedPtr& operator=(const ScopedPtr&) = delete;
};

int main() {
    // 创建一个 ScopedPtr 对象 'entity',它将管理一个新创建的 Entity 对象。
    // 使用直接初始化语法,这是创建智能指针的推荐方式。
    ScopedPtr entity(new Entity());

    // 通过重载的 operator->() 访问 Entity 对象的成员函数 Print()
    // 编译时,entity->Print() 会被翻译为 (entity.operator->())->Print();
    entity->Print();

    // 访问 Entity 对象的成员变量 x (通过 operator->())
    entity->x = 20; // 修改 x 的值
    entity->Print(); // 再次打印,确认 x 已被修改

    // 暂停程序,等待用户输入,以便观察输出和析构过程
    std::cin.get();

    // main 函数结束时,'entity' 对象会离开作用域,
    // 其 ScopedPtr 类的析构函数会自动被调用,从而自动释放它所管理的 Entity 对象,避免内存泄漏。
    return 0;
}

看着代码学习

重载->运算符

解析 entityPtr->Print(); 这行代码的执行过程:

  1. 当编译器看到 entityPtr->Print() 时,它知道 entityPtr 是一个 ScopedPtr 类的对象,而不是一个裸指针。
  2. 于是,编译器会寻找 ScopedPtr 类中是否有 operator->() 的重载。
  3. 它找到了你定义的 Entity* operator->() 函数。
  4. 编译器会调用 entityPtr.operator->()
  5. entityPtr.operator->() 执行,并返回 entityPtr 内部存储的那个 Entity* 类型的裸指针(即 m_Obj 的值)。
  6. 然后,编译器会拿这个返回的 Entity* 裸指针,再对其应用 ->Print() 操作,最终调用到 Entity 类的 Print() 函数。

用箭头运算符获取内存中某值的偏移量笔记

  • 目的: 这种技巧主要用于在编译时计算结构体(或类)成员相对于该结构体(或类)起始地址的偏移量。

  • 核心思想: 利用 C/C++ 编译器在处理结构体成员访问时的特性,即编译器在编译阶段就已知结构体的内存布局和成员的相对位置。它不会真的去解引用一个空指针,而是计算成员的偏移量。

  • 语法示例:

    cpp 复制代码
    struct Vector3
    {
        float x, y, z;
    };
    
    int main()
    {
        // 计算 Vector3 结构体中成员 z 的偏移量
        int offset = (int)(&((Vector3*)nullptr)->z); // 1749396158901.png
        std::cout << offset << std::endl;
        // ...
    }
  • 解析步骤:

    1. (Vector3*)nullptr : 将空指针 nullptr 强制转换为指向 Vector3 类型的指针。
    2. ((Vector3*)nullptr)->z : 使用箭头运算符 -> 访问 Vector3 结构体中的成员 z。此时编译器会根据 Vector3 的定义,确定 z 相对于结构体起始地址的偏移量。
    3. &(...) : & 是取地址运算符,它获取的是 z 成员的地址。由于我们是基于 nullptr(地址 0)进行的操作,这个"地址"实际上就是 z 相对于结构体起始的偏移量。
    4. (int) : 将计算得到的偏移量(通常是 size_t 类型)强制转换为 int 类型以便打印。
相关推荐
笨笨马甲26 分钟前
Qt Quick模块功能及架构
开发语言·qt
乄夜29 分钟前
嵌入式面试高频(5)!!!C++语言(嵌入式八股文,嵌入式面经)
c语言·c++·单片机·嵌入式硬件·物联网·面试·职场和发展
夜晚回家41 分钟前
「Java基本语法」代码格式与注释规范
java·开发语言
YYDS3141 小时前
C++动态规划-01背包
开发语言·c++·动态规划
前端页面仔1 小时前
易语言是什么?易语言能做什么?
开发语言·安全
树叶@1 小时前
Python数据分析7
开发语言·python
wydaicls1 小时前
十一.C++ 类 -- 面向对象思想
开发语言·c++
Biomamba生信基地2 小时前
R语言基础| 下载、安装
开发语言·r语言·生信·医药
姜君竹2 小时前
QT的工程文件.pro文件
开发语言·c++·qt·系统架构
思捻如枫2 小时前
C++数据结构和算法代码模板总结——算法部分
数据结构·c++