跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++

C++ 类与对象基础详解(上)

综述由AI生成讲解 C++ 类与对象的基础知识,包括类定义格式、成员变量与函数、访问限定符、类域、实例化概念、内存对齐规则及 this 指针原理。通过栈(Stack)和日期(Date)类的代码示例,阐述了对象内存分配、函数共享机制及 this 指针如何区分不同对象,适合初学者理解面向对象核心概念。

信号故障发布于 2026/3/25更新于 2026/5/3128 浏览

一、类定义格式

类定义的基本格式:

在 C++ 中,使用 class 关键字来定义类,其基本格式如下:

class Stack // class 为定义类的关键字,Stack 为类名 {
    // 左大括号表示类体的开始
    // 类体内容
}; // 右大括号后的分号不能省略,这是类定义结束的标志

类的成员:

类体中包含的内容统称为类的成员,主要分为两类:

  • 成员变量(属性):类中定义的变量,用于描述对象的状态
  • 成员函数(方法):类中定义的函数,用于描述对象的行为

成员变量的命名规范:

为了便于区分成员变量与局部变量,业界通常采用以下命名惯例(非强制要求,具体遵循所在团队的编码规范):

  • 在成员变量名前加下划线,如:_top、_size
  • 在成员变量名后加下划线,如:top_、size_
  • 以"m"开头命名,如:mTop、mSize

struct 与 class 的关系:

在 C++ 中,struct 也被升级为类,主要体现在:

  • 兼容 C 语言中 struct 的传统用法
  • 扩展功能:struct 中可以定义成员函数
  • 主要区别:默认访问权限不同(struct 默认为 public,class 默认为 private)

虽然 struct 也能定义类,但通常推荐使用 class 关键字来定义类,这样更能体现面向对象的封装特性。

成员函数的默认特性:

定义在类体内的成员函数默认会被编译器当作内联函数(inline)处理,这有助于提高短小函数的调用效率。

二、访问限定符

访问限定符与封装

C++ 通过访问限定符来实现面向对象的封装特性。封装将对象的属性(成员变量)和方法(成员函数)结合在类内部,并通过访问权限控制,有选择性地向外部使用者暴露接口,隐藏内部实现细节。

三种访问限定符

  • public(公有):修饰的成员在类内外都可以直接访问,通常用于对外提供的接口
  • protected(保护):修饰的成员在类外不能直接访问,但在派生类(子类)中可以访问
  • private(私有):修饰的成员仅在当前类内部可以访问,类外部和派生类都不能直接访问

注:在继承章节之前,protected 和 private 的表现相同,都是类外不可访问;只有在涉及继承时,两者的区别才会体现出来——protected 成员可以被派生类访问,而 private 成员不能。

访问权限的作用域规则

  • 访问限定符的作用域从该限定符出现的位置开始,直到下一个访问限定符出现时结束
  • 如果某个访问限定符之后没有其他访问限定符,其作用域将持续到类定义结束的右大括号 }

默认访问权限

  • 使用 class 定义的类:成员默认访问权限为 private
  • 使用 struct 定义的类:成员默认访问权限为 public

编程实践建议

在实际开发中,通常遵循以下设计原则:

  • 成员变量:一般声明为 private 或 protected,隐藏内部状态,通过公有成员函数提供访问接口
  • 成员函数:对外提供的功能接口设为 public,内部辅助函数设为 private 或 protected

三、类域

类域的概念

在 C++ 中,类定义了一个新的作用域,称为类域(Class Scope)。类的所有成员(包括成员变量和成员函数)都位于这个作用域内。这意味着:

  • 成员名可以在类内部直接使用
  • 在类外部访问成员时,需要通过对象、指针或作用域解析运算符 :: 来指定所属的类

类域外定义成员

当需要在类体外定义成员函数时,必须使用作用域解析运算符 :: 来指明该函数属于哪个类域,语法格式为:返回类型 类名::函数名 (参数列表)

#include <iostream>
using namespace std;

// 定义 Stack 类
class Stack {
public:
    // 成员函数声明(在类体内)
    void Init(int n);
    void Push(int val);
    void Print();
private:
    int* array; // 动态数组
    int capacity; // 容量
    int top; // 栈顶指针
};

// 在类体外定义成员函数,必须使用类域限定符
void Stack::Init(int n) {
    capacity = n;
    array = new int[capacity];
    top = 0;
    cout << "栈已初始化,容量:" << capacity << endl;
}

void Stack::Push(int val) {
    if (top < capacity) {
        array[top++] = val;
        cout << "入栈:" << val << endl;
    }
}

void Stack::Print() {
    cout << "栈内元素:";
    for (int i = 0; i < top; i++) {
        cout << array[i] << " ";
    }
    cout << endl;
}

int main() {
    Stack st;
    st.Init(5); // 通过对象调用成员函数
    st.Push(10);
    st.Push(20);
    st.Push(30);
    st.Print();
    return 0;
}

四、实例化概念

实例化的概念

在 C++ 中,用类类型在物理内存中创建对象的过程,称为类的实例化。实例化是面向对象编程中的核心概念,它将抽象的类定义转化为具体的、可用的对象。

类与对象的关系

  • 类:是对对象的抽象描述,相当于一个蓝图或模型。类中定义的成员变量只是声明,并没有分配实际的物理内存空间。
  • 对象:是类的具体实例,通过实例化过程创建。对象占用实际的物理内存,存储类中定义的成员变量。

实例化的特点

  1. 空间分配:只有实例化对象时,系统才会为成员变量分配内存空间
  2. 多实例性:一个类可以实例化出多个独立的对象,每个对象都有自己的成员变量存储空间
  3. 成员函数共享:成员函数在代码区存储,被该类的所有对象共享
#include <iostream>
using namespace std;

// 类的定义(相当于设计图)
class Stack {
public:
    int* array; // 成员变量声明(此时未分配空间)
    int capacity;
    int top;
};

int main() {
    // 类就像设计图,本身不占用存储数据的空间
    // sizeof 计算的是类型的大小,不是实际存储空间
    cout << "Stack 类的大小:" << sizeof(Stack) << " 字节" << endl;

    // 实例化对象:用类类型在内存中创建对象
    Stack st1; // st1 是 Stack 类的一个实例(对象)
    Stack st2; // st2 是另一个独立的对象

    // 对象占用实际物理内存,存储成员变量
    cout << "对象 st1 的大小:" << sizeof(st1) << " 字节" << endl;
    cout << "对象 st2 的大小:" << sizeof(st2) << " 字节" << endl;

    // 每个对象都有自己的独立存储空间
    st1.capacity = 10;
    st2.capacity = 20;
    cout << "st1.capacity: " << st1.capacity << endl;
    cout << "st2.capacity: " << st2.capacity << endl;
    return 0;
}

五、对象大小和内存对齐规则

在 C++ 中,类实例化出的每个对象都有独立的数据空间,但对象中只存储成员变量,不存储成员函数。

为什么成员函数不存储在对象中?

  1. 函数的本质:函数被编译后是一段指令,存储在代码段(Code Segment)中,而不是存储在对象的内存空间内。
  2. 没有必要存储函数指针:
    • 假设在对象中存储成员函数指针,那么每个对象都要存储相同的函数地址
    • 如果实例化 100 个对象,相同的函数指针就会被重复存储 100 次,造成极大的空间浪费
  3. 编译时确定地址:
    • 普通成员函数的调用在编译链接时就已经确定了函数地址
    • 编译后的汇编指令为 call 地址,这个地址是在编译链接阶段确定的
    • 因此不需要在运行时通过对象查找函数地址
  4. 特例说明:
    • 只有动态多态(虚函数)需要在运行时确定函数地址
    • 这时需要通过虚函数表(vtable)来存储函数地址

内存对齐规则

  • 第一个成员在与结构体偏移量为 0 的地址处。
  • 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
  • 注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
  • VS 中默认的对齐数为 8。
  • 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
  • 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

六、this 指针

为什么需要 this 指针?

当我们定义一个类时,成员函数只有一份代码拷贝,但可能会被多个对象调用。那么问题来了:当对象 d1 调用 Init 和 Print 函数时,函数是如何知道应该访问 d1 的成员变量还是 d2 的成员变量呢?

答案是:C++ 通过隐含的 this 指针来解决这个问题。

this 指针的工作原理

  1. 编译器的处理
    编译器在编译后,会在类的每个非静态成员函数的第一个参数位置,自动添加一个当前类类型的指针参数,这就是 this 指针。
  2. 原理解析
    • d1.Init(2024, 1, 1) 实际上被编译器转换为 Date::Init(&d1, 2024, 1, 1)
    • 成员函数中对成员变量的访问,如 _year = year,实际上被转换为 this->_year = year
  3. this 指针的特性
    • this 指针是 const 指针(Date* const this),不能修改指向
    • 不能在实参和形参位置显式写 this 指针
    • 可以在函数体内显式使用 this 指针
#include <iostream>
using namespace std;

class Date {
private:
    int _year;
    int _month;
    int _day;
public:
    // 成员函数 - 编译器实际会转换为:
    // void Init(Date* const this, int year, int month, int day)
    void Init(int year, int month, int day) {
        // 这里的_year 实际是 this->_year
        _year = year; // 等价于 this->_year = year
        _month = month; // 等价于 this->_month = month
        _day = day; // 等价于 this->_day = day
        // 可以在函数体内显式使用 this 指针
        cout << "this 指针的地址:" << this << endl;
    }

    // 成员函数 - 编译器实际会转换为:
    // void Print(Date* const this)
    void Print() {
        // 显式使用 this 指针访问成员变量
        cout << "对象地址:" << this << endl;
        cout << "日期:" << this->_year << "-" << this->_month << "-" << this->_day << endl;
        // 也可以隐式访问,效果相同
        // cout << "日期:" << _year << "-" << _month << "-" << _day << endl;
    }

    // 演示 this 指针的用途:比较两个日期是否相等
    bool IsSameMonth(const Date& d) {
        // this 指向当前对象,&d 是参数对象的地址
        return (this->_month == d._month);
    }

    // 返回当前对象的引用(链式编程常用)
    Date& SetYear(int year) {
        this->_year = year;
        return *this; // 返回对象本身
    }
};

int main() {
    cout << "=== this 指针演示 ===" << endl;
    // 创建两个不同的对象
    Date d1;
    Date d2;
    cout << "d1 的地址:" << &d1 << endl;
    d1.Init(2024, 1, 1); // &d1 被隐式传递给 this 指针
    d1.Print();
    cout << "\nd2 的地址:" << &d2 << endl;
    d2.Init(2024, 6, 15); // &d2 被隐式传递给 this 指针
    d2.Print();

    // 演示 this 指针如何区分对象
    cout << "\n=== 比较月份 ===" << endl;
    if (d1.IsSameMonth(d2)) {
        cout << "d1 和 d2 是同一个月" << endl;
    } else {
        cout << "d1 和 d2 不是同一个月" << endl;
    }

    // 演示 this 指针的链式调用
    cout << "\n=== 链式调用演示 ===" << endl;
    d1.SetYear(2025).Print(); // SetYear 返回*this,可以继续调用 Print
    return 0;
}

目录

  1. 一、类定义格式
  2. 二、访问限定符
  3. 三、类域
  4. 类域的概念
  5. 类域外定义成员
  6. 四、实例化概念
  7. 实例化的概念
  8. 类与对象的关系
  9. 实例化的特点
  10. 五、对象大小和内存对齐规则
  11. 为什么成员函数不存储在对象中?
  12. 内存对齐规则
  13. 六、this 指针
  14. 为什么需要 this 指针?
  15. this 指针的工作原理
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 多线程数据竞争解析:互斥锁与原子操作原理及实践
  • Qwen2.5 大模型微调实践指南:基于 LLaMA-Factory 与魔塔社区
  • Codex 代码生成模型简介
  • 前端项目打包与部署指南:从 npm run build 到线上发布
  • OpenClaw 全平台卸载指南:Windows、macOS、Linux 及包管理器清理
  • Linux System V 标准简介
  • Python 修改 pip 默认安装路径的几种方法
  • Python 拒绝采样算法优化与多峰分布模拟实战
  • 大模型高频面试题精选与核心考点解析
  • GitHub 全界面中文化:Tampermonkey 插件安装与配置指南
  • ROS 2 DDS 中间件通信优化与 QoS 策略详解
  • 安卓手机使用 Termux 部署 AstrBot 与 NapCat 搭建 QQ 机器人
  • Java 并发核心:单例、生产者消费者、定时器与线程池实战
  • RuoYi-Vue Pro 基于 Spring Boot 与 Vue 的企业级快速开发平台
  • 解决 git 推送出现 fatal: The remote end hung up unexpectedly 错误的 3 种方法
  • 大模型技术系统学习书单推荐:从理论到实践
  • AI 辅助前端设计:从原型验证到代码生成的全流程
  • Stable Diffusion WebUI 本地部署教程
  • 大模型并非过时,小模型与多模型协作是未来趋势
  • Vivado 管脚分配实战指南:从原理到避坑全解析

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online