news 2026/6/25 21:18:32

【C++类和对象】从结构体到类、构造函数与析构函数详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【C++类和对象】从结构体到类、构造函数与析构函数详解

1. 类的定义 —— class vs struct

C++ 中,classstruct都可以用来定义类。

class ClassName

{
// 成员变量(属性)
// 成员函数(方法)
}; // 分号不能省略

区别

代码示例与用法归类

  • class中成员默认是private(私有)

  • struct中成员默认是public(公有)

  • C++ 升级了 struct,里面可以放函数

  • struct ListNodeCPP {
    void Init(int x) {
    next = nullptr;
    val = x;
    }
    ListNodeCPP* next;
    int val;
    }; // 不再需要 typedef,ListNodeCPP 本身就是类型名

  • 建议:一般情况下,用class定义类,用struct定义纯数据聚合(如链表节点)。

  • 成员变量的命名习惯

    为了区分成员变量和普通变量,常见约定:在成员变量

  • 后面加_year_

  • 前面加__year

  • 前面加mm_year

  • class Date {
    private:
    int _year; // 前面加 _
    int _month;
    int _day;
    };

    2. 访问限定符 —— 封装的第一步

    限定符类外访问说明
    public✅ 可以对外接口
    protected❌ 不可以继承相关,暂时和 private 一样
    private❌ 不可以内部实现细节
    class Date {
    public: // 从这里开始,之后的成员对外可见
    void Init(int year, int month, int day);
    private: // 从这里开始,之后的成员对外隐藏
    int _year;
    int _month;
    int _day;
    };
  • 作用域:从该限定符出现的位置开始,到下一个限定符或类结束为止。
    class默认privatestruct默认public
  • 3. 类域 —— 影响编译查找规则

    类定义了一个新的作用域。在类体外定义成员函数时,需要用::指明属于哪个类:

  • class Date {
    public:
    void Init(int year, int month, int day);
    private:
    int _year;
    int _month;
    int _day;
    };

    // 类外定义 → 必须指定类域 Date::
    void Date::Init(int year, int month, int day) {
    _year = year; // 在当前函数作用域找不到 _year,会去 Date 类域中找
    _month = month;
    _day = day;
    }

  • 如果不指定Date::,编译器会把Init当成全局函数,找不到_year等成员就会报错!

  • 【补充:::的用法与原理详解】

    在刚才的代码中,我们使用了void Date::Init(...),这里的::称为作用域解析运算符(Scope Resolution Operator)。它是 C++ 中非常重要的符号,直接决定了编译器去哪里查找名字。

    1.::的用法(左边是什么?右边是什么?)

    ::的左右两侧分工明确: 域::具体成员

  • 左侧(左操作数):指定要查找的“域”(Scope)。它可以是类名(如Date)、命名空间名(如std),或者留空(空着表示全局作用域)。

  • 右侧(右操作数):该域内的具体成员。包括成员函数名(如Init)、成员变量名类型名(如size_t)或静态成员

2.::的原理(编译器如何处理)

核心原理::的核心作用就是强制指定查找路径,它会命令编译器“跳过”默认的层层查找规则,直接去指定的域里找。

默认查找规则(不加::时)
编译器遵循就近原则(即名字查找规则):先在当前局部域(函数体内)找,找不到再去全局域找。如果局部有同名变量,编译器绝对不会去全局找。

使用::后的查找规则

  1. 编译器解析到A::B时,会先将A当作一个限定符,识别A到底是一个还是一个命名空间(如果是::B,则直接定位到全局域)。

  2. 确定A的作用域范围后,编译器只在该作用域内部的符号表中搜索B。如果B在该作用域中不存在,编译器会直接报错(“未定义标识符”),而不会再去外部的全局域碰运气。通俗的讲,A::B,可以粗略理解为“去A中找B”

为什么要这样设计(意义)?

  • 解决名字冲突(隐藏问题):当局部变量与全局变量重名时,用::变量名可以精准指名道姓,让编译器不再受“就近原则”干扰。

  • 实现声明与定义分离(类外定义):在类体外定义成员函数时(如void Date::Init()),::告诉编译器:“InitDate家族的成员”。如果不加::,编译器会认为你在定义一个全局函数Init,那么函数内部访问_year时,编译器会去全局找,自然就找不到私有成员,从而报错。

  • 访问命名空间成员:通过std::cout的方式,可以将庞大的标准库隔离在std域中,避免与用户自定义的cout发生冲突。

  • 4. 实例化与对象大小

    4.1 实例化 —— 从图纸到房子

    就像一张设计图,规定了有哪些房间(成员变量),但本身不占用物理空间。
    对象是根据设计图建造出来的房子,真正占用内存空间。

  • class Date {
    private:
    int _year; // 声明,未开空间
    int _month;
    int _day;
    };

    int main() {
    Date d1; // 实例化,此时才分配空间
    Date d2; // 可以实例化多个对象
    return 0;
    }

    4.2 对象大小 —— 内存对齐规则

    对象中只存储成员变量,不存储成员函数(函数在代码段)。

    C++ 规定对象大小遵循内存对齐规则(VS 默认对齐数为 8):

  • 第一个成员在偏移量为 0 的地址。

  • 其他成员对齐到对齐数的整数倍地址。
    对齐数 =min(成员大小, 默认对齐数) 取成员大小与默认对齐数更小的那个

  • 结构体总大小为最大对齐数的整数倍。

  • 不同数据类型在常见平台下占用的字节数:

  • 数据类型32 位环境(字节)64 位环境(字节)说明
    char11字符类型,固定 1 字节
    bool11布尔类型,固定 1 字节
    short22短整型,固定 2 字节
    int44整型,固定 4 字节
    long48(Linux)/4(Windows)⚠️ 视平台而定,Windows 64 位下 long 仍然是 4 字节
    long long88长长整型,固定 8 字节
    float44单精度浮点,固定 4 字节
    double88双精度浮点,固定 8 字节
    指针T*48指针大小 = 地址总线宽度:32 位系统 4 字节,64 位系统 8 字节
  • class A {
    private:
    char _ch; // 1 字节,偏移 0
    int _i; // 4 字节,对齐数 4,从偏移 4 开始 → 中间填充 3 字节
    }; // 总大小 = 8 字节(最大对齐数为 4,8 是 4 的倍数)总大小为整数倍

    class B {}; // 空类,大小为1占位标识对象存在)

    5. 构造函数 —— 自动初始化

    5.1 为什么需要构造函数?

    之前我们写DateStack时,需要手动调用Init()函数来初始化对象。
    构造函数让对象在实例化时自动调用,不需要用户手动初始化。

    5.2 构造函数的特点

    特点说明
    函数名与类名相同Date()Stack()
    无返回值不需要写void
    自动调用对象实例化时自动执行
    可以重载支持多个构造函数
    默认生成用户不写,编译器自动生成一个
  • 什么是“默认构造函数”?

    有人可能会误以为“默认构造”就是编译器自动生成的那个。实际上,默认构造函数的定义是:

    不传实参就可以调用的构造函数

    它包含以下三种

    类型示例说明
    ① 无参构造函数Date()用户显式定义,无参数
    ② 全缺省构造函数Date(int y = 1, int m = 1, int d = 1)所有参数都有默认值
    ③ 编译器自动生成的无参构造用户完全不写任何构造函数时对内置类型不做初始化
  • 这三种默认构造函数无法共存!
  • 先把一个注释掉
  • 全缺省构造和普通带参构造,在类外定义的函数体没有任何区别

    唯一的区别在于类内声明时:全缺省写了=默认值,普通带参没写。

    既然函数体一模一样,而全缺省构造既能不传参、又能传部分、又能传全部,它已经完全覆盖了普通带参构造的功能,所以你根本不需要再写一个普通带参构造,只写全缺省一个就足够了!

    6. 析构函数 —— 自动清理资源

    6.1 为什么需要析构函数?

    构造函数负责初始化,析构函数负责资源清理(释放动态分配的内存、关闭文件等)。
    对象生命周期结束时,析构函数自动调用,不需要手动调用。

  • class Stack {
    public:
    Stack(int n = 4) {
    _a = (int*)malloc(sizeof(int) * n);
    _capacity = n;
    _top = 0;
    }

    ~Stack() { // 析构函数:~类名
    free(_a);
    _a = nullptr;
    _top = _capacity = 0;
    }

    private:
    int* _a;
    size_t _capacity;
    size_t _top;
    };

    6.2 析构函数的特点

    特点说明
    函数名~类名,如~Date()~Stack()
    无参数不能重载,一个类只有一个析构函数
    无返回值不需要写void
    自动调用对象生命周期结束时自动执行
    默认生成用户不写,编译器自动生成
  • 6.3 编译器自动生成的析构函数

  • 内置类型:不做处理

  • 自定义类型成员:调用该成员的析构函数

    6.4 什么时候需要自己写析构?

    类类型是否需要显示写析构原因
    Date❌ 不需要没有资源申请,编译器默认即可
    Stack必须写内部有malloc申请的资源,必须free

    6.5 多个对象的析构顺序

    C++ 规定:后定义的对象先析构(类似于栈,后进先出)。

  • 7. C 与 C++ 的 Stack 对比(示意)

  • 维度C 语言实现C++ 类实现
    数据和函数分离封装在一起
    初始化//initialization需要手动调用Init()构造函数自动调用
    资源释放需要手动调用Destroy()析构函数自动调用
    访问控制无法控制,任意访问private隐藏内部细节
    类型名需要typedef简化类名本身就是类型
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/25 21:18:34

符号计算在F4分级群与立方Jordan代数验证中的应用实践

1. 项目概述:当抽象代数遇上符号计算如果你在数学物理或者理论计算机的圈子里待过一阵子,肯定对“验证”这个词又爱又恨。尤其是在处理像F4分级群(F4 Graded Group)这类高度抽象的代数结构时,手动推导一个公式、验证一…

作者头像 李华
网站建设 2026/6/25 21:18:33

密码学实战:从古典密码到AES的破解与安全实践

1. 项目概述:一次从古至今的密码学实战之旅“密码学实战:从古典密码到AES,手把手教你破解常见加密算法”这个标题,听起来就充满了动手的乐趣和挑战性。它不是一个枯燥的理论讲座,而是一场穿越密码学历史的实战演练。作…

作者头像 李华
网站建设 2026/6/25 21:18:31

如何3分钟搞定网盘高速下载:开源直链助手终极指南

如何3分钟搞定网盘高速下载:开源直链助手终极指南 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼云盘…

作者头像 李华
网站建设 2026/6/25 21:18:28

OWASP安全速查表技术架构解析:从Git协作到静态站点生成

1. 项目概述:当安全指南遇上工程实践在应用安全领域,OWASP(开放式Web应用安全项目)的Cheat Sheet Series(速查表系列)几乎是每个开发者和安全工程师的“案头必备”。我们经常在遇到一个具体的安全问题时&am…

作者头像 李华
网站建设 2026/6/25 21:18:19

终极Markdown Viewer浏览器插件:三分钟打造专业文档阅读环境

终极Markdown Viewer浏览器插件:三分钟打造专业文档阅读环境 【免费下载链接】markdown-viewer Markdown Viewer / Browser Extension 项目地址: https://gitcode.com/gh_mirrors/ma/markdown-viewer 还在为浏览器中无法优雅预览Markdown文件而烦恼吗&#x…

作者头像 李华