C++ 类和对象详解(1)

类和对象是 C++ 面向对象编程的核心概念,它们为代码提供了更好的封装性、可读性和可维护性。本文将从类的定义开始,逐步讲解访问限定符、类域、实例化、对象大小计算、this 指针等关键知识,并对比 C 语言与 C++ 在实现数据结构时的差异,快速掌握类和对象的基础用法。

1. 类的定义

类是对现实事物的抽象描述,它封装了数据(成员变量)和操作数据的方法(成员函数)。在 C++ 中,我们通过class关键字定义类,其基本结构如下:

1.1 类定义格式

class为定义类的关键字,Stack为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省
略。类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或
者成员函数。

C++中struct也可以定义类,C++兼容C中struct的用法,同时struct升级成了类,明显的变化是
struct中可以定义函数,一般情况下我们还是推荐用class定义类。

定义在类面的成员函数默认为 inline 函数,声明和定义分离就不是了。

class 类名 {// 成员变量(属性)// 成员函数(方法)
}; // 注意:分号不能省略

示例:Stack 类
下面是一个栈(Stack)类的定义:

#include<iostream>
#include<assert.h>
#include<stdlib.h>
using namespace std;class Stack {
public:// 成员函数:初始化栈void Init(int n = 4) {array = (int*)malloc(sizeof(int) * n);if (nullptr == array) {perror("malloc申请空间失败");return;}capacity = n; // 容量top = 0; // 栈顶指针(指向待插入位置)}// 成员函数:入栈void Push(int x) {// 扩容逻辑(简化)array[top++] = x;}// 成员函数:取栈顶元素int Top() {assert(top > 0); // 确保栈非空return array[top - 1];}// 成员函数:销毁栈void Destroy() {free(array);array = nullptr;top = capacity = 0;}private:// 成员变量(加_区分普通变量)int* array; // 存储数据的数组size_t capacity; // 容量size_t top; // 栈顶指针
};

1.2 成员命名习惯

为了区分成员变量和普通变量,通常会在成员变量前加_m_(如_yearm_capacity)。这不是 C++ 的强制规定,而是工程中的常见惯例,便于代码阅读。

示例:Date 类

class Date {
public:void Init(int year, int month, int day) {_year = year;   // 成员变量_year_month = month; // 成员变量_month_day = day;     // 成员变量_day}private:int _year;  // 年份int _month; // 月份int _day;   // 日期
};

1.3 class 与 struct 的区别

C++ 中的struct也可以定义类,它与class的主要区别是:

  • class默认访问权限为private(私有);
  • struct默认访问权限为public(公有),且兼容 C 语言中struct的用法(如仅作为数据结构)。

实际开发中,推荐用class定义类,struct多用于纯数据结构(如链表节点)。

示例:C++ 的 struct 类

// C++中struct可定义函数
struct ListNodeCPP 
{void Init(int x){ // 成员函数:初始化节点val = x;next = nullptr;}ListNodeCPP* next; // 成员变量int val;
};//C++中不用 typedef //补充:C语言中
typedef struct ListNodeC
{int val;struct ListNodeC* next;
}LN;

2. 访问限定符

访问限定符用于控制类成员在类外的访问权限,是 C++ 实现封装的核心手段。C++ 提供三种访问限定符:

2.1 访问权限规则

限定符权限说明
public类内、类外均可访问(公有)
private仅类内可访问,类外不可直接访问(私有)
protected类内可访问,子类可访问,类外不可直接访问(继承时体现差异)(私有)

访问权限的作用域从该限定符出现的位置开始,直到下一个限定符或类结束(})为止。

2.2 封装的意义

通过访问限定符,我们可以将成员变量设为private,仅通过public的成员函数操作数据,避免外部代码随意修改数据导致的错误。例如 Stack 类中,arraytop等变量被private保护,外部只能通过PushTop等函数操作,确保栈的逻辑正确。

3. 类域

类定义了一个独立的作用域(类域),类的所有成员都属于这个作用域。在类外定义成员函数时,需要用::(作用域操作符)指明所属的类。

3.1 类外定义成员函数

如果成员函数的声明和定义分离(类内声明,类外定义),必须通过类名::函数名指定类域,否则编译器会将函数视为全局函数。

示例:类外定义 Stack::Init

class Stack {
public:void Init(int n = 4); // 类内声明
private:int* array;size_t capacity;size_t top;
};// 类外定义,需指定Stack类域
void Stack::Init(int n) {array = (int*)malloc(sizeof(int) * n);if (nullptr == array) {perror("malloc失败");return;}capacity = n;top = 0;
}

3.2 类域的作用

类域确保了不同类的成员可以重名而不冲突。例如,两个类都可以有Init函数,通过类域区分:Stack::InitDate::Init。(注意:类域和命名空间域只影响名字的隔离,不影响生命周期)

4. 类的实例化

用类创建对象的过程称为实例化。类本身只是一个 “模板”,不占用实际内存(可以说是一个声明,声明不占空间);实例化后的对象才会分配内存,存储成员变量。

4.1 实例化的意义

  • 类:抽象的 “设计图”,描述对象有哪些属性和方法(如 “房子设计图”);
  • 对象:根据类创建的具体实例,占用内存,可存储数据(如 “用设计图盖的房子”)。

示例:实例化 Date 对象

int main() {Date d1; // 用Date类实例化对象d1Date d2; // 实例化对象d2d1.Init(2024, 3, 31); // 调用d1的Init方法d2.Init(2024, 7, 5);  // 调用d2的Init方法return 0;
}

4.2 对象存储的内容

对象中只存储成员变量,成员函数被所有对象共享(存储在代码段)。原因是:

  • 成员变量是每个对象的独立数据(如 d1 和 d2 的_year可以不同);
  • 成员函数是相同的操作逻辑(如 d1 和 d2 的Init函数代码完全一样,无需重复存储)。

5. 对象大小计算

5.1 对象存储方式

函数被编译后是一段指令,对象中没办法存储,这些指令存储在一个单独的区域(代码段),那么对象中非要存储的话,只能是成员函数的指针。

再分析一下,对象中是否有存储指针的必要呢,Date实例化d1和d2两个对象,d1和d2都有各自独立的成员变量_year/_month/_day存储各自的数据,但是d1和d2的成员函数 Init 指针却是一样的,存储在对象中就浪费了。

如果用Date实例化100个对象,那么成员函数指针就重复存储100次,太浪费了。其实函数指针是不需要存储的,函数指针是一个地址,调用函数被编译成汇编指令[call 地址], 其实编译器在编译链接时,就要找到函数的地址,不是在运行时找,只有动态多态是在运行时找,就需要存储函数地址。

对象的大小由成员变量决定,遵循内存对齐规则;成员函数不占用对象的内存。

5.2 内存对齐规则

  1. 第一个成员在偏移量为 0 的地址;
  2. 其他成员对齐到 “对齐数” 的整数倍地址(对齐数 = min (编译器默认对齐数8,成员大小),VS 默认对齐数为 8);
  3. 对象总大小是最大对齐数的整数倍;
  4. 嵌套结构体时,嵌套对象对齐到自身最大对齐数的整数倍,总大小包含嵌套对象的对齐数。

5.3 示例:计算对象大小

class A {
public:void Print() { cout << _ch << endl; }
private:char _ch; // 大小1int _i;   // 大小4
};class B {
public:void Print() {} // 无成员变量
};class C {}; // 空类int main() {A a;B b;C c;cout << sizeof(a) << endl; // 8(1+3填充+4)cout << sizeof(b) << endl; // 1(占位,标识对象存在)cout << sizeof(c) << endl; // 1(空类大小为1)return 0;
}

如果不对齐,则会导致编译器读取,出现错误。(编译器规定每次读的时候,是从固定的整数倍开始读固定字节的,为的是提高机器效率)

  • 类 A:_ch(1 字节)+ 3 字节填充(对齐到 4)_i(4 字节),总 8 字节;
  • 类 B 和 C:无成员变量,但为了标识对象存在,占用 1 字节。

6. this 指针

当多个对象调用同一个成员函数时,函数如何区分操作的是哪个对象?C++ 通过隐藏的this指针解决这个问题。

6.1 this 指针的特性

  • this指针是成员函数的隐含参数,类型为类名* const(指向当前对象,不可修改指向);
  • 调用成员函数时,编译器自动将对象地址作为this指针传入;
  • 函数体内可显式使用this指针访问成员变量(如this->_year = year),但不能在形参或实参中显式声明。
  • C++规定不能在实参和形参的位置显示的写this指针(编译时编译器会处理),但是可以在函数体内显示使用this指针。

示例:this 指针的隐含传递

年月日,本质是通过 this 指针访问的,this 不可修改:

#include<iostream>
using namespace std;
class Date
{
public:// void Init(Date* const this, int year, int month, int day)void Init(int year, int month, int day){this->_year = year;this->_month = month;this->_day = day;}// void Print(Date* const this)void Print(){cout << this->_year << "/" << this->_month << "/" << _day << endl;}private:// 这里只是声明,没有开空间int _year;int _month;int _day;
};int main()
{// Date类实例化出对象d1和d2Date d1;Date d2;// d1.Init(&d1, 2024, 3, 31);d1.Init(2024, 3, 31);// d1.Print(&d1);d1.Print();// d2.Init(&d2, 2024, 7, 5);d2.Init(2024, 7, 5);// d2.Print(&d2);d2.Print();return 0;
}

6.2 经典问题:空指针调用函数

问题 1:以下代码运行结果?

class A {
public:void Print() { cout << "A::Print()" << endl; }
private:int _a;
};int main() {A* p = nullptr;p->Print(); // 正常运行?崩溃?return 0;
}

答案:正常运行

解答:

Print函数未访问成员变量(无需解引用this),即使p为 nullptr,仍可调用。

class a
{
public:void print(){cout << this << endl;cout << "a::print()" << endl;}
private:int _a;
};int main()
{a* p = nullptr;// mov ecx  pp->print(); // call  地址//p->_a = 1;a aa;return 0;
}

this 是指向对象的地址,指向对象的指针,是为了方便访问成员变量的,这里 p 就是对象的地址。

问题 2:以下代码运行结果?

class A {
public:void Print() { cout << _a << endl; } // 访问成员变量
private:int _a;
};int main() {A* p = nullptr;p->Print(); // 正常运行?崩溃?return 0;
}

答案:运行崩溃

解答:

Print函数访问_a(即this->_a),而this为 nullptr,解引用空指针导致崩溃。

问题3:this指针存在内存哪个区域的 ()

A. 栈 B.堆 C.静态区 D.常量区 E.对象里面

答案:A. 栈

解答:

this 是形参,参数在栈帧中。

7. C 与 C++ 实现 Stack 对比

面向对象三大特性:封装、继承、多态

面向对象的封装特性让 C++ 的代码更简洁、安全。对比 C 语言和 C++ 实现的 Stack:

特性C 语言实现C++ 实现
数据与方法分离(如STInitST结构体)封装在类中(成员变量 + 成员函数)
访问控制无限制(可直接修改ST的成员)private保护成员,仅通过public函数访问
调用方式需显式传递结构体地址(如STPush(&s, 1)隐含this指针(如s.Push(1)
代码可读性需记住函数与结构体的关联类内函数直接操作成员,逻辑清晰

C 语言实现核心代码

typedef struct Stack {int* a;int top;int capacity;
} ST;void STInit(ST* ps) { // 需显式传参ps->a = NULL;ps->top = 0;ps->capacity = 0;
}void STPush(ST* ps, int x) { // 需显式传参// 入栈逻辑
}

C++ 实现核心代码

class Stack {
public:void Init(int n = 4) { // 隐含this指针// 初始化逻辑}void Push(int x) { // 隐含this指针// 入栈逻辑}
private:int* _a;int _top;int _capacity;
};

C++,相当于来说,更加规范,因为进行了封装:

一个直观的感受,访问栈顶元素:

C语言中:

但是,在C++中:

是不能实现的。

在 C++ 面向对象编程中,类的默认成员函数是实现封装、简化代码的核心机制。当我们未显式定义某些成员函数时,编译器会自动生成它们,以保证类的基本功能可用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/95372.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/95372.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

奈飞工厂:算法优化实战

推荐系统的算法逻辑与优化技巧在流媒体行业的 “用户注意力争夺战” 中&#xff0c;推荐系统是决定成败的核心武器。对于拥有2.3 亿全球付费用户的奈飞&#xff08;Netflix&#xff09;而言&#xff0c;其推荐系统每天处理数十亿次用户交互&#xff0c;最终实现了一个惊人数据&…

【人工智能99问】BERT的训练过程和推理过程是怎么样的?(24/99)

文章目录BERT的训练过程与推理过程一、预训练过程&#xff1a;学习通用语言表示1. 数据准备2. MLM任务训练&#xff08;核心&#xff09;3. NSP任务训练4. 预训练优化二、微调过程&#xff1a;适配下游任务1. 任务定义与数据2. 输入处理3. 模型结构调整4. 微调训练三、推理过程…

[TryHackMe]Challenges---Game Zone游戏区

这个房间将涵盖 SQLi&#xff08;手动利用此漏洞和通过 SQLMap&#xff09;&#xff0c;破解用户的哈希密码&#xff0c;使用 SSH 隧道揭示隐藏服务&#xff0c;以及使用 metasploit payload 获取 root 权限。 1.通过SQL注入获得访问权限 手工注入 输入用户名 尝试使用SQL注入…

北京JAVA基础面试30天打卡09

1.MySQL存储引擎及区别特性MyISAMMemoryInnoDBB 树索引✅ Yes✅ Yes✅ Yes备份 / 按时间点恢复✅ Yes✅ Yes✅ Yes集群数据库支持❌ No❌ No❌ No聚簇索引❌ No❌ No✅ Yes压缩数据✅ Yes❌ No✅ Yes数据缓存❌ NoN/A✅ Yes加密数据✅ Yes✅ Yes✅ Yes外键支持❌ No❌ No✅ Yes…

AI时代的SD-WAN异地组网如何落地?

在全球化运营与数字化转型浪潮下&#xff0c;企业分支机构、数据中心与云服务的跨地域互联需求激增。传统专线因成本高昂、部署缓慢、灵活性差等问题日益凸显不足。SD-WAN以其智能化调度、显著降本、敏捷部署和云网融合的核心优势&#xff0c;成为实现高效、可靠、安全异地组网…

css中的color-mix()函数

color-mix() 是 CSS 颜色模块&#xff08;CSS Color Module Level 5&#xff09;中引入的一个强大的颜色混合函数&#xff0c;用于在指定的颜色空间中混合两种或多种颜色&#xff0c;生成新的颜色值。它解决了传统颜色混合&#xff08;如通过透明度叠加&#xff09;在视觉一致性…

Github desktop介绍(GitHub官方推出的一款图形化桌面工具,旨在简化Git和GitHub的使用流程)

文章目录**1. 简化 Git 操作****2. 代码版本控制****3. 团队协作****4. 代码托管与共享****5. 集成与扩展****6. 跨平台支持****7. 适合的使用场景****总结**GitHub Desktop 是 GitHub 官方推出的一款图形化桌面工具&#xff0c;旨在简化 Git 和 GitHub 的使用流程&#xff0c;…

整数规划-分支定界

内容来自:b站数学建模老哥 如:3.4,先找小于3的,再找大于4的 逐个

JetPack系列教程(六):Paging——让分页加载不再“秃”然

前言 在Android开发的世界里&#xff0c;分页加载就像是一场永无止境的马拉松&#xff0c;每次滚动到底部&#xff0c;都仿佛在提醒你&#xff1a;“嘿&#xff0c;朋友&#xff0c;还有更多数据等着你呢&#xff01;”但别担心&#xff0c;Google大佬们早就看透了我们的烦恼&a…

扎实基础!深入理解Spring框架,解锁Java开发新境界

大家好&#xff0c;今天想和大家聊聊Java开发路上绕不开的一个重要基石——Spring框架。很多朋友在接触SpringBoot、SpringCloud这些现代化开发工具时&#xff0c;常常会感到吃力。究其原因&#xff0c;往往是对其底层的Spring核心机制理解不够透彻。Spring是构建这些高效框架的…

Heterophily-aware Representation Learning on Heterogeneous Graphs

Heterophily-Aware Representation Learning on Heterogeneous Graphs (TPAMI 2025) 计算机科学 1区 I:18.6 top期刊 📌 摘要 现实世界中的图结构通常非常复杂,不仅具有全局结构上的异质性,还表现出局部邻域内的强异质相似性(heterophily)。虽然越来越多的研究揭示了图…

计算机视觉(7)-纯视觉方案实现端到端轨迹规划(思路梳理)

基于纯视觉方案实现端到端轨迹规划&#xff0c;需融合开源模型、自有数据及系统工程优化。以下提供一套从模型选型到部署落地的完整方案&#xff0c;结合前沿开源技术与工业实践&#xff1a; 一、开源模型选型与组合策略 1. 感知-预测一体化模型 ViP3D&#xff08;清华&#…

Nginx 屏蔽服务器名称与版本信息(源码级修改)

Nginx 屏蔽服务器名称与版本信息&#xff08;源码级修改&#xff09; 一、背景与目的 在生产环境部署 Nginx 时&#xff0c;默认配置会在 Server 响应头中暴露服务类型&#xff08;如 nginx&#xff09;和版本号&#xff08;如 nginx/1.25.4&#xff09;。这些信息可能被攻击者…

从钢板内部应力视角,重新认识护栏板矫平机

一、为什么钢板会“自带波浪”&#xff1f; 钢卷在热轧后冷却、卷取、长途运输、多次吊运时&#xff0c;不同部位受到的温度、张力、碰撞并不一致&#xff0c;内部会产生不均匀的残余应力。应力大的区域想“伸长”&#xff0c;应力小的区域想“缩短”&#xff0c;宏观上就表现为…

C++中的`auto`与`std::any`:功能、区别与选择建议

引言 在C编程中&#xff0c;auto和std::any是两个功能强大但用途不同的工具。理解它们的区别和适用场景对于编写高效、可维护的代码至关重要。本文将详细介绍auto和std::any的基本概念、使用方法、适用场景以及它们之间的区别&#xff0c;并提供选择建议&#xff0c;帮助开发者…

【Linux】进程(Process)

一、什么是进程二、进程的创建三、进程的状态四、僵尸进程五、孤儿进程六、进程的优先级 以及 并发/并行七、进程的切换一、什么是进程&#xff1f;什么是进程呢(一)?官方话来说&#xff1a;进程是一个执行实例、正在执行的程序、是系统资源分配的基本单位按课本官方话可能有一…

销售管理系统哪个好?14款软件深度对比

本文将深入对比14款销售管理系统&#xff1a;1.纷享销客&#xff1b; 2.Zoho CRM&#xff1b; 3.神州云动 CRM&#xff1b; 4.励销云 CRM&#xff1b; 5.Microsoft Dynamics 365 CRM&#xff1b; 6.悟空 CRM&#xff1b; 7.泛微 CRM&#xff1b; 8.HubSpot CRM&#xff1b; 9.…

如何从 0 到 1 开发企业级 AI 应用:步骤、框架与技巧

本文来自作者 莫尔索 的 企业级 AI 应用开发与最佳实践指南&#xff0c; 欢迎阅读原文。 大家好&#xff0c;我之前出版的《LangChain 编程&#xff1a;从入门到实践》一书获得了良好的市场反响和读者认可。近期推出了第二版&#xff0c;我对内容进行了大幅更新&#xff1a;近 …

【LLM】Openai之gpt-oss模型和GPT5模型

note gpt-oss模型代理能力&#xff1a;使用模型的原生功能进行函数调用、网页浏览&#xff08;https://github.com/openai/gpt-oss/tree/main?tabreadme-ov-file#browser&#xff09;、Python 代码执行&#xff08;https://github.com/openai/gpt-oss/tree/main?tabreadme-o…

Ubuntu 20.04 虚拟机安装完整教程:从 VMware 到 VMware Tools

目录 一、VMware的安装 1. 资源获取 1. 网盘提取 2. VMware官网&#xff0c;选择自己合适的版本&#xff0c;我下载的是16.2版本 2.安装步骤 二、Ubuntu的安装 1. Ubuntu 镜像文件官网下载 2. Ubuntu的安装步骤 第一步&#xff1a;打开刚刚安装好的VMware16.2.0&#…