【C++篇】类与对象(上篇):从面向过程到面向对象的跨越

news2025/4/1 8:29:48

💬 欢迎讨论:在阅读过程中有任何疑问,欢迎在评论区留言,我们一起交流学习!
👍 点赞、收藏与分享:如果你觉得这篇文章对你有帮助,记得点赞、收藏,并分享给更多对C++感兴趣的朋友!


文章目录

    • 前言
    • 一、 面向过程 vs 面向对象
    • 二、类
      • 1. 类的引入
      • 2. 类的定义
      • 3. 类的访问限定符与封装
        • 3.1 访问限定符
        • 3.2 封装
      • 4. 类的作用域
    • 三、对象
      • 1. 类的实例化(定义对象)
      • 2. 类对象模型
        • 计算类对象的大小
      • 3. this指针的奥秘
        • 3.1 this指针的引出
        • 3.2 this指针的特性
    • 总结


前言

大家好,我是“我想吃余”,很高兴你能和我一起进入到C++的学习中,我会将我的学习过程中的宝贵经验不遗余力的输入到文章中,希望可以帮助到你的学习。本文涵盖了从面向过程与面向对象的区别,到类的定义、访问限定符、封装、作用域、实例化、对象大小计算,以及this指针等内容。


一、 面向过程 vs 面向对象

  • 面向过程(C语言):关注解决问题的步骤,通过函数逐步实现。

举个洗衣服的例子:手洗衣服的步骤
在这里插入图片描述

  • 面向对象(C++):关注对象之间的交互,将问题拆分为多个对象协作完成。

机洗衣服:在这里插入图片描述

核心区别:面向对象通过对象交互隐藏细节,提高代码复用性和可维护性。


二、类

1. 类的引入

  • C语言的结构体:只能定义成员变量。
  • C++的类(class/struct):可以定义成员变量和成员函数。
    // C++实现栈(使用class)
    class Stack {
    public:
        void Init() { /* 初始化逻辑 */ }
        void Push(int data) { /* 压栈逻辑 */ }
    private:
        int* _array;
        int _size;
    };
    

2. 类的定义

class className
{
// 类体:由成员函数和成员变量组成
};  // 一定要注意后面的分号

class为定义类的关键字ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。

类体中内容称为类的成员:类中的变量称为类的属性成员变量; 类中的函数称为类的方法或者成员函数

类的两种定义方式

  1. 声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。

在这里插入图片描述
showlnfo函数可能会被编译器当成内联函数

  1. 类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::

在这里插入图片描述

💡:这是更推荐的定义方式,因为可以提高代码可读性和编译效率。

成员变量命名规则的建议:

在给成员变量命名时,在其前面加一个_或者一个m

原因:避免与函数形参重命名导致的可读性差的问题。


3. 类的访问限定符与封装

3.1 访问限定符

C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。

  • 访问限定符
    在这里插入图片描述

    1. public修饰的成员在类外可以直接被访问
    2. protected/private修饰的成员在类外不能直接被访问
    3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
    4. 如果后面没有访问限定符,作用域就到 } 即类结束
    5. class的默认访问权限为privatestructpublic(因为struct要兼容C)

💡:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别

3.2 封装

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

什么是封装呢?

封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。

其实封装本质上是一种管理,让用户更方便使用类。

封装的意义:将数据与操作结合,隐藏实现细节,仅暴露接口。

class Date {
public:
    void SetYear(int year) { _year = year; } // 对外提供接口
private:
    int _year; // 隐藏实现细节
};

举两个例子:

  1. 汽车:汽车是一个十分复杂的一个设备,其内部的复杂系统原理我们并不需要去了解,因为我们不是要造车,而是要开车。只需要会用方向盘、挂挡和油门刹车,它们就类似于封装暴露出来的接口,剩余的都被封装保护起来了,我们看不见摸不着。让用户可以与车子进行交互即可。

在这里插入图片描述
2. 计算机:对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的,CPU内部是如何设计的等,用户只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此计算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可

在这里插入图片描述


4. 类的作用域

类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 ::作用域操作符指明成员属于哪个类域。

  • 类作用域:成员函数在类外定义时需指定作用域。
class Person
{
public:
void PrintPersonInfo();
private:
char _name[20];
char _gender[3];
int  _age;
};
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{
cout << _name << " "<< _gender << " " << _age << endl;
 }
 
  • 实例化:类像“设计图”,对象是“具体建筑”。
    Person p;         // 实例化对象
    p._name = "Tom";  // 对象占用实际内存
    

三、对象

1. 类的实例化(定义对象)

用类类型创建对象的过程,称为类的实例化

  1. 类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它
  2. 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量

💡:定义一个类,并没有给其对象开辟内存空间,实例化才是定义对象和开辟内存空间

一个十分形象的比方:类是一张“房子的设计图”,对象是一座“房子”,实例化就是“用设计图造一座房子”
在这里插入图片描述


2. 类对象模型

计算类对象的大小

计算方式与计算结构体大小方法相同,都是用结构体对齐来计算的。

  • 对象大小计算规则
    • 仅计算成员变量之和,遵循内存对齐规则。
    • 成员函数不纳入计算
    • 空类大小为1字节(占位标识)。
  • 内存对齐规则
    1. 第一个成员在与结构体偏移量为0的地址处。
    2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
      • 注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
      • VS中默认的对齐数为8
    3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
    4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
    class A {
        char _a;    // 1字节(对齐到1)
        int _b;     // 4字节(对齐到4)
    };  // 总大小:8字节(1+3填充+4)
    

具体计算方法不过多赘述了,存有疑问的话建议阅读我过去的文章:高阶C语言|和结构体与位段的邂逅之旅


3. this指针的奥秘

3.1 this指针的引出

作用:隐式指向调用成员函数的对象,解决“如何区分不同对象”的问题。

现在看不懂没关系,我们来看一段代码:
我们定义一个日期类 Date

#include<iostream>
using namespace std;
class Date
{
	public:
	void Init(int year, int month, int day)
	{
		_year = year;
		this->_month = month;
		this->_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
	private:
	// 这⾥只是声明,没有开空间
	int _year;
	int _month;
	int _day;
};

int main()
{
	// Date类实例化出对象d1和d2
	Date d1;
	Date d2;
	d1.Init(2024, 3, 31);
	d1.Print();
	d2.Init(2024, 7, 5);
	d2.Print();
	return 0;
}

对于上述类,我们会有这样的一个问题:

Date类中有 Init Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?

C++中通过引入this指针解决该问题:
C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。

如果将this指针显示出来,其实是这样的:

#include<iostream>
using namespace std;
class Date
{
	public:
	void Init(Date* const this, int year, int month, int day)
	{
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}
	void Print(Date* const this)
	{
		cout << this->_year << "/" << this->_month << "/" << this->_day << endl;
	}
	private:
	// 这⾥只是声明,没有开空间
	int _year;
	int _month;
	int _day;
};

int main()
{
	// Date类实例化出对象d1和d2
	Date d1;
	Date d2;
	d1.d1.Init(&d1, 2024, 3, 31);
	d1.Print(&d1);
	d2.Init(&d2, 2024, 7, 5);
	d2.Print(&d2);
	return 0;
}

在这里插入图片描述
在这里插入图片描述

3.2 this指针的特性
  1. this指针的类型:类型* const ,即成员函数中,不能给this指针赋值。
  2. 只能在“成员函数”的内部使用
  3. this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针
  4. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递

💡: this不能在形参和实参显示传递,但是可以在函数内部显示使用

  • this指针存在哪里呢?
    因为this本质是成员函数的形参,所以它必然存储在栈区
  • this指针可以为空吗?
    形参当然可以为空,但是要注意成员函数不能对其解引用操作,否则会运行崩溃。

总结

面向对象的核心在于封装,通过类将数据与方法结合,隐藏细节并提供接口。理解this指针、内存对齐和访问控制是掌握C++类与对象的关键。


通过这篇博客,希望能帮助你系统理解类和对象的基础概念,为后续学习打下坚实基础!下一篇的主要内容是六大默认成员函数,敬请期待一下吧~🥰

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=aa76nyr0pb

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

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

相关文章

智慧运维平台:赋能未来,开启高效运维新时代

在当今数字化浪潮下&#xff0c;企业IT基础设施、工业设备及智慧城市系统的复杂度与日俱增&#xff0c;传统人工运维方式已难以满足高效、精准、智能的管理需求。停机故障、低效响应、数据孤岛等问题直接影响企业运营效率和成本控制。大型智慧运维平台&#xff08;AIOps, Smart…

基于大语言模型的智能音乐创作系统——从推荐到生成

一、引言&#xff1a;当AI成为音乐创作伙伴 2023年&#xff0c;一款由大语言模型&#xff08;LLM&#xff09;生成的钢琴曲《量子交响曲》在Spotify冲上热搜&#xff0c;引发音乐界震动。传统音乐创作需要数年专业训练&#xff0c;而现代AI技术正在打破这一壁垒。本文提出一种…

Reactive编程:什么是Reactive编程?Reactive编程思想

文章目录 **1. Reactive编程概述****1.1 什么是Reactive编程&#xff1f;****1.1.1 Reactive编程的定义****1.1.2 Reactive编程的历史****1.1.3 Reactive编程的应用场景****1.1.4 Reactive编程的优势** **1.2 Reactive编程的核心思想****1.2.1 响应式&#xff08;Reactive&…

深度剖析:U盘突然无法访问的数据拯救之道

一、引言 在数字化办公与数据存储日益普及的当下&#xff0c;U盘凭借其小巧便携、即插即用的特性&#xff0c;成为了人们工作、学习和生活中不可或缺的数据存储工具。然而&#xff0c;U盘突然无法访问这一棘手问题却时常困扰着广大用户&#xff0c;它不仅可能导致重要数据的丢失…

蓝桥杯-特殊的三角形(dfs/枚举/前缀和)

思路分析 深度优先搜索&#xff08;DFS&#xff09;思路 定义与参数说明 dfs 函数中&#xff0c;last 记录上一条边的长度&#xff0c;用于保证新选边长度大于上一条边&#xff0c;实现三边互不相等 。cnt 记录已选边的数量&#xff0c;当 cnt 达到 3 时&#xff0c;就构成了…

一文详解k8s体系架构知识

0.云原生 1.k8s概念 1. k8s集群的两种管理角色 Master&#xff1a;集群控制节点&#xff0c;负责具体命令的执行过程。master节点通常会占用一股独立的服务器&#xff08;高可用部署建议用3台服务器&#xff09;&#xff0c;是整个集群的首脑。 Master节点一组关键进程&#xf…

wx162基于springboot+vue+uniapp的在线办公小程序

开发语言&#xff1a;Java框架&#xff1a;springbootuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#…

Baklib内容中台的核心优势是什么?

智能化知识管理引擎 Baklib的智能化知识管理引擎通过多源数据整合与智能分类技术&#xff0c;实现企业知识资产的自动化归集与动态更新。系统内置的语义分析算法可自动识别文档主题&#xff0c;结合自然语言处理技术生成结构化标签体系&#xff0c;大幅降低人工标注成本。针对…

【C++】C++11介绍列表初始化右值引用和移动语义

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. C11简介2. 统一的列表初始化2.1&#xff5b;&#xff5d;初始化2.2 std::initializer_list 3. 声明3.1 auto3.2 decltype3.3 nullptr 4. 范围for循环4.1 范围for的语法4.2 范围for的使用条件 5. STL中一些变化6. 右…

搜广推校招面经六十一

美团推荐算法 一、ANN算法了解么&#xff1f;说几种你了解的ANN算法 ANN 近似最近邻搜索&#xff08;Approximate Nearest Neighbor Search&#xff09;算法 1.1. KD-Tree&#xff08;K-Dimensional Tree&#xff0c;K 维树&#xff09; 类型: 空间划分数据结构适用场景: 低…

人工智能与软件工程结合的发展趋势

AI与软件工程的结合正在深刻改变软件开发的流程、工具和方法&#xff0c;其发展方向涵盖了从代码生成到系统维护的整个生命周期。以下是主要的发展方向和技术趋势&#xff1a; 1. 软件架构体系的重构 从“面向过程”到“面向目标”的架构转型&#xff1a; AI驱动软件设计以目标…

nacos 外置mysql数据库操作(docker 环境)

目录 一、外置mysql数据库原因&#xff1a; 二、数据库准备工作 三、构建nacos容器 四、效果展示 一、外置mysql数据库原因&#xff1a; 想知道nacos如何外置mysql数据库之前&#xff0c;我们首先要知道为什么要外置mysql数据库&#xff0c;或者说这样做有什么优点和好处&am…

【数电】半导体存储电路

组合逻辑电路输入和输出之间是确定关系&#xff0c;与之前的历史记录没有任何关系。时序逻辑电路则有相应的存储元件&#xff0c;要把之前的状态保存起来。 要构成时序逻辑电路&#xff0c;必须要有相应的存储元件&#xff0c;第五章讲述相应的存储元件 一、半导体存储电路概…

Jenkins插件安装失败如何解决

问题&#xff1a;安装Jenkins时候出现插件无法安装的情况。 测试环境&#xff1a; 操作系统&#xff1a;Windows11 Jenkins&#xff1a;2.479.3 JDK&#xff1a;17.0.14&#xff08;21也可以&#xff09; 解决办法一&#xff1a; 更换当前网络&#xff0c;局域网、移动、联通…

postman测试文件上传接口详解

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 postman是一个很好的接口测试软件&#xff0c;有时候接口是Get请求方式的&#xff0c;肯定在浏览器都可以测了&#xff0c;不过对于比较规范的RestFul接口&#x…

什么是贴源库

贴源库的定义与核心概念 贴源库&#xff08;Operational Data Store, ODS&#xff09;是数据架构中的基础层&#xff0c;通常作为数据仓库或数据中台的第一层&#xff0c;负责从业务系统直接抽取、存储原始数据&#xff0c;并保持与源系统的高度一致性。其核心在于“贴近源头”…

UE5中开启ACES工作流程

首先要开启OCIO插件 OpenColorIO 创建配置 下载ACES https://github.com/colour-science/OpenColorIO-Configs/tree/feature/aces-1.2-config 加载ACES的ocio 选择Srgb 选择ACES 参考链接: https://zhuanlan.zhihu.com/p/534357694 https://www.youtube.com/watch?vBo3Bvh…

基于springboot+vue的农产品电商平台

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

解决Dubbo3调用Springcloud接口报No provider available from registry RegistryDirectory

解决Dubbo调用Springcloud接口报No provider available from registry RegistryDirectory 问题发现问题解决 问题发现 在学习Dubbo过程中&#xff0c;Dubbo官网有一篇文章《微服务最佳实践&#xff0c;零改造实现 Spring Cloud & Apache Dubbo 互通》&#xff0c;跟着示例…

2023第十四届蓝桥杯大赛软件赛国赛C/C++ 大学 B 组(真题题解)(C++/Java题解)

本来想刷省赛题呢&#xff0c;结果一不小心刷成国赛了 真是个小迷糊〒▽〒 但&#xff0c;又如何( •̀ ω •́ )✧ 记录刷题的过程、感悟、题解。 希望能帮到&#xff0c;那些与我一同前行的&#xff0c;来自远方的朋友&#x1f609; 大纲&#xff1a; 一、子2023-&#xff…