【C++】2. 类和对象(上)

文章目录

  • 一、类的定义
    • 1、类定义格式
    • 2、访问限定符
    • 3、类域
  • 二、实例化
    • 1、实例化概念
    • 2、对象⼤⼩
  • 三、this指针
  • 四、C++和C语⾔实现Stack对⽐

一、类的定义

1、类定义格式

  • class为定义类的关键字,Stack为类的名字,{ }中为类的主体,注意类定义结束时后⾯分号不能省略。类体中内容称为类的成员,类中的变量称为类的属性或成员变量类中的函数称为类的⽅法或者成员函数
#include<iostream>
using namespace std;class Stack
{
public://成员函数void Push(int x){}void Pop(){}int Top(){return 0;}private://成员变量int* a;int top;int capacity;};int main()
{Stack st;st.Push(1);st.Pop();return 0;
}
  • 为了区分成员变量,⼀般习惯上成员变量会加⼀个特殊标识,如成员变量前⾯或者后⾯加_或者 m开头,注意C++中这个并不是强制的,只是⼀些惯例。

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

#include<iostream>
using namespace std;struct Date
{
public:void Init(int year, int month, int day);private://用_标识成员变量  声明int _year;int _month;int _day;
};void Date::Init(int year, int month, int day)
{_year = year;_month = month;_day = day;
}int main()
{struct Date d1;Date d2;Date d3;d2.Init(2025, 8, 1);return 0;
}
#include<iostream>
using namespace std;//兼容C的struct用法
typedef struct ListNodeC
{int val;struct ListNodeC* next;
}LTNodeC;//CPP
struct ListNodeCPP
{int val;ListNodeCPP* next;
};int main()
{ListNodeCPP node;return 0;
}
  • 定义在类里⾯的成员函数默认为inline。

2、访问限定符

在这里插入图片描述

  • C++通过⼀种实现封装的⽅式,⽤类将对象的属性与⽅法结合在⼀块,让对象更加完善,通过访问权限选择性的将其接⼝提供给外部的⽤⼾使⽤。

  • public修饰的成员在类外可以直接被访问。protected和private修饰的成员在类外不能直接被访问。

  • 访问权限作⽤域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为⽌,如果后⾯没有访问限定符,作⽤域就到 }(类结束)。

  • class定义的成员未被访问限定符修饰时默认为private,struct默认为public。

  • ⼀般成员变量都会被限制为private/protected,需要给别⼈使⽤的成员函数会放为public。

3、类域

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

  • 类域影响的是编译的查找规则,下⾯程序中Init如果不指定类域Stack,那么编译器就把Init当成全局函数,那么编译时,找不到array等成员的声明/定义在哪⾥,就会报错。指定类域Stack,就是知道Init是成员函数,当前域找不到的array等成员,就会到类域中去查找。

#include<iostream>
using namespace std;class Stack
{
public:void Init(int n = 4);private:int* _a;int _top;int _capacity;
};void Stack::Init(int n)
{_a = (int*)malloc(sizeof(int) * n);if (_a == nullptr){perror("malloc fail");return;}_capacity = n;_top = 0;
}int main()
{Stack st;st.Init();return 0;
}

二、实例化

1、实例化概念

  • ⽤类类型在物理内存中创建对象的过程,称为类实例化出对象。

  • 类是对对象进⾏⼀种抽象描述,是⼀个模型⼀样的东西,限定了类有哪些成员变量,这些成员变量只是声明,没有分配空间,⽤类实例化出对象时,才会分配空间。

  • ⼀个类可以实例化出多个对象,实例化出的对象占⽤实际的物理空间,存储类成员变量。打个⽐⽅:类实例化出对象就像现实中使⽤建筑设计图建造出房⼦,类就像是设计图,设计图规划了有多少个房间,房间⼤⼩功能等,但是并没有实体的建筑存在,也不能住⼈,⽤设计图修建出房⼦,房⼦才能住⼈。同样类就像设计图⼀样,不能存储数据,实例化出的对象分配物理内存存储数据。
    在这里插入图片描述

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

2、对象⼤⼩

类实例化出的每个对象,都有独⽴的数据空间,所以对象中肯定包含成员变量,那么是否包含成员函数呢?⾸先函数被编译后是⼀段指令,对象中没办法存储,这些指令存储在⼀个单独的区域(代码段),因此只能存储成员函数的指针。再分析⼀下,对象中是否有存储指针的必要呢,Date实例化d1和d2两个对象,d1和d2都有各⾃独⽴的成员变量_year/_month/_day存储各⾃的数据,但是d1和d2的成员函数Init/Print指针却是⼀样的,存储在对象中就浪费了。如果⽤Date实例化100个对象,那么成员函数指针就重复存储100次,太浪费了。

还要注意,其实函数指针是不需要存储的,函数指针是⼀个地址,调⽤函数被编译成汇编指令[call 地址],其实编译器在编译链接时,就要找到函数的地址,不是在运⾏时找,只有动态多态是在运⾏时找,就需要存储函数地址。

上⾯我们分析了对象中只存储成员变量,C++规定类实例化的对象也要符合内存对⻬的规则。
内存对⻬规则:
①:第⼀个成员位于结构体偏移量为0的地址处。

②:其他成员变量要对⻬到对⻬数整数倍的地址处。

注意:对⻬数=编译器默认的⼀个对⻬数与该成员⼤⼩的较⼩值,VS中默认的对⻬数为8。

③:嵌套的结构体对齐到自己成员中最大对齐数的整数倍。

④ :结构体总⼤⼩为:最⼤对⻬数(含嵌套结构体的对齐数)的整数倍。

#include<iostream>
using namespace std;// 计算⼀下A/B/C实例化的对象是多大? 
class A
{
public:void Print(){cout << _ch << endl;}private:char _ch;int _i;
};class B
{
public:void Print(){//...}
};class C
{};int main()
{A a;B b;C c;cout << sizeof(a) << endl;cout << sizeof(b) << endl;cout << sizeof(c) << endl;return 0;
}

实例化对象a的大小计算:
在这里插入图片描述

运行结果:
在这里插入图片描述

上⾯的程序运⾏后,我们看到没有成员变量的B和C类对象的⼤⼩是1,为什么没有成员变量还要给1个字节呢?因为如果⼀个字节都不给,怎么表⽰对象存在过呢!所以这⾥给1字节,纯粹是为了占位标识对象存在。

三、this指针

  • Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调⽤Init和Print函数时,该函数是如何知道应该访问的是d1对象还是d2对象呢?那么这⾥就要看到C++给了⼀个隐含的this指针解决这⾥的问题。

  • 编译器编译后,类的成员函数默认都会在形参第⼀个位置,增加⼀个当前类类型的指针,叫做this指针。⽐如Date类的Init的真实原型为: void Init(Date* const this, int year, int month, int day)

  • 类的成员函数中访问成员变量,本质都是通过this指针访问的,如Init函数中给_year赋值, this->_year = year

  • C++规定不能在实参和形参的位置显⽰的写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 << _year << "/" << _month << "/" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1;Date d2;//d1.Init(&d1,2025,5,1)d1.Init(2025, 5, 1);//d1.Print(&d1)d1.Print();//d2.Init(&d2,2025,10,1)d2.Init(2025, 10, 1);//d2.Print(&d2)d2.Print();return 0;
}

在这里插入图片描述

四、C++和C语⾔实现Stack对⽐

⾯向对象三⼤特性:封装、继承、多态,下⾯的对⽐我们可以初步了解⼀下封装。
通过下⾯两份代码对⽐,我们发现C++实现Stack形态上还是发⽣了挺多的变化,底层和逻辑上没啥变化。

在这里插入图片描述

  • C++中数据和函数都放到了类⾥⾯,通过访问限定符进⾏了限制,不能再随意通过对象直接修改数据,这是C++封装的⼀种体现,这个是最重要的变化。这⾥封装的本质是⼀种更严格规范的管理,避免出现乱访问修改的问题。

  • C++中有⼀些相对⽅便的语法,⽐如Init给的缺省参数会⽅便很多,成员函数每次不需要传对象地址,因为this指针隐含的传递了,使⽤类型不再需要typedef来命名类名就很⽅便。

C实现Stack:

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>typedef int STDataType;
typedef struct Stack
{STDataType* a;int top;int capacity;
}ST;void STInit(ST* ps)
{assert(ps);ps->a = NULL;ps->top = ps->capacity = 0;
}void STDestroy(ST* ps)
{assert(ps);free(ps->a);ps->a = NULL;ps->top = ps->capacity = 0;
}void STPush(ST* ps, STDataType x)
{assert(ps);//检查空间大小if (ps->top == ps->capacity){int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));if (tmp == NULL){perror("realloc fail");return;}ps->a = tmp;ps->capacity = newcapacity;}//入栈ps->a[ps->top] = x;ps->top++;
}bool STEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}void STPop(ST* ps)
{assert(ps);assert(!STEmpty(ps));ps->top--;
}STDataType STTop(ST* ps)
{assert(ps);assert(!STEmpty(ps));return ps->a[ps->top - 1];
}int STSize(ST* ps)
{assert(ps);return ps->top;
}int main()
{ST s;STInit(&s);STPush(&s, 1);STPush(&s, 2);STPush(&s, 3);STPush(&s, 4);while (!STEmpty(&s)){printf("%d\n", STTop(&s));STPop(&s);}STDestroy(&s);return 0;
}

C++实现Stack:

#include<iostream>
#include<assert.h>
#include<stdbool.h>
using namespace std;typedef int STDataType;
class Stack
{
public://成员函数void Init(int n = 4){_a = (STDataType*)malloc(sizeof(STDataType) * n);if (_a == nullptr){perror("malloc fail");return;}_capacity = n;_top = 0;}void Push(STDataType x){//检查空间大小if (_top == _capacity){int newcapacity = _capacity * 2;STDataType* tmp = (STDataType*)realloc(_a, sizeof(STDataType) * newcapacity);if (tmp == NULL){perror("realloc fail");return;}_a = tmp;_capacity = newcapacity;}//入栈_a[_top++] = x;}void Pop(){assert(_top > 0);--_top;}bool Empty(){return _top == 0;}int& Top(){assert(_top > 0);return _a[_top - 1];}void Destroy(){free(_a);_a = nullptr;_top = _capacity = 0;}private://成员变量STDataType* _a;int _capacity;int _top;
};int main()
{Stack s;s.Init();s.Push(1);s.Push(2);s.Push(3);s.Push(4);while (!s.Empty()){printf("%d\n", s.Top());s.Pop();}s.Destroy();return 0;
}

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

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

相关文章

UnityURP 扭曲屏幕效果实现

UnityURP 扭曲屏幕效果实现前言项目下载URPGrabPass空间扭曲着色器实现添加可视化控制创建材质球并设置补充粒子使用步骤CustomData映射移动设备优化鸣谢前言 在Unity的Universal Render Pipeline (URP) 中&#xff0c;传统的GrabPass功能被移除&#xff0c;借助URPGrabPass工…

(三)软件架构设计

2024年博主考软考高级系统架构师没通过&#xff0c;于是决定集中精力认真学习系统架构的每一个环节&#xff0c;并在2025年软考中取得了不错的成绩&#xff0c;虽然做信息安全的考架构师很难&#xff0c;但找对方法&#xff0c;问题就不大&#xff01; 本文主要是博主在学习过程…

切记使用mt19937构造随机数

在做 Kazaee CodeForces - 1746F 这个问题的时候&#xff0c;最初的时候使用了ran()&#xff0c;然后一直WA&#xff0c;遂改成mt19937&#xff0c;顺利通过本道题。 mt19937 Rand(time(0)); 调用随机数时候&#xff0c;使用&#xff1a; Rand() & 1 注意看&#xff0…

基于N32G45x+RTT驱动框架的定时器外部计数

时钟选择 高级控制定时器的内部时钟:CK_INT: 两种外部时钟模式: 外部输入引脚 外部触发输入 ETR 内部触发输入(ITRx):一个定时器用作另一个定时器的预分频器 外部时钟原理 通过配置 TIMx_SMCTRL.SMSEL=111 选择该模式。 计数器可以配置为在所选输入的时钟上升沿或下降沿 …

[特殊字符] Ubuntu 下 MySQL 离线部署教学(含手动步骤与一键脚本)

适用于 Ubuntu 20.04 / 22.04 无网络环境部署 MySQL。 建议初学者先按手动方式部署一遍理解原理&#xff0c;再使用自动化脚本完成批量部署。&#x1f4c1; 一、准备工作 ✅ 1. 虚拟机环境 系统&#xff1a;Ubuntu 22.04&#xff08;或兼容版本&#xff09;环境&#xff1a;无网…

系统一个小时多次Full GC,导致系统线程停止运行,影响系统的性能,可靠性

背景&#xff1a; 某一天系统出现了请求超时&#xff0c;然后通过日志查看&#xff0c;程序执行到某一个位置&#xff0c;直接停下来来了&#xff0c;或者说所有的线程的执行都停下来了。而且是该时间段&#xff0c;请求处理变慢。排查相关的服务&#xff0c;并没有出现死锁&am…

使用OMV+NextCloud搭建私有云

原文地址&#xff1a;使用OMVNextCloud搭建私有云 – 无敌牛 欢迎参观我的网站&#xff1a;无敌牛 – 技术/著作/典籍/分享等 OpenMediaVault&#xff08;简称OMV&#xff09;是一款基于Debian的开源网络存储&#xff08;NAS&#xff09;操作系统&#xff0c;提供Web管理界面&…

Codeforces Round 1008 (Div. 2)

A. Final Verdict 题目大意 给你一个数组a&#xff0c;每次把他拆分为等长的k个子序列&#xff0c;然后用子序列的平均数替换掉这个子序列&#xff0c;问最后能不能让数组只剩下一个数字x 解题思路 无论怎么划分&#xff0c;最后的总值是不变的&#xff0c;所以只需要看总和…

python转移安装目录到D盘

迁移python安装路径第一步&#xff1a;移动目录第二步&#xff1a;修改环境变量之前没有设置之前设置过第一步&#xff1a;移动目录 源路径&#xff1a; C:\Users\Emma.ZRF\AppData\Local\Programs\Python\Python38 原环境变量 C:\Users\Emma.ZRF\AppData\Local\Programs\Pyth…

C#垃圾回收机制:原理与实践

C#垃圾回收机制:原理与实践 一、垃圾回收:C#内存管理的“幕后功臣”​ 二、GC的核心引擎:基于代的优化策略 三、Demo展示 1. 简单对象的垃圾回收示例 2. 基于代的回收示例 四、常用方法 五、推荐使用的场景 六、注意事项 管住手:避免滥用 GC.Collect() 析构函数:保持轻量 …

基于SpringBoot+MyBatis+MySQL+VUE实现的名城小区物业管理系统(附源码+数据库+毕业论文+开题报告+部署教程+配套软件)

摘要 当下&#xff0c;正处于信息化的时代&#xff0c;许多行业顺应时代的变化&#xff0c;结合使用计算机技术向数字化、信息化建设迈进。以前相关行业对于物业信息的管理和控制&#xff0c;采用人工登记的方式保存相关数据&#xff0c;这种以人力为主的管理模式已然落后。本人…

3DXML 转换为 UG 的技术指南及迪威模型网在线转换推荐

一、3DXML 转换为 UG 的必要性 &#xff08;一&#xff09;软件功能利用需求 3DXML 格式由达索系统开发&#xff0c;主要用于在其相关产品&#xff08;如 CATIA、SOLIDWORKS 和 3DEXPERIENCE 等&#xff09;中进行 3D 数据交换与轻量化可视化。它虽然能够很好地在达索生态内实…

无人机光伏巡检缺陷检出率↑32%:陌讯多模态融合算法实战解析

原创声明本文为原创技术解析&#xff0c;引用来源标注 “陌讯技术白皮书”&#xff0c;禁止未经授权的转载与改编。摘要在无人机光伏巡检场景中&#xff0c;边缘计算优化与复杂场景鲁棒性是提升检测效率的核心挑战。本文解析陌讯多模态融合算法在光伏板热斑、隐裂等缺陷检测中的…

仓库管理系统-15-前端之管理员管理和用户管理

文章目录 1 后台查询用户列表 1.1 null和空字符串的检查 1.2 UserController.java 2 管理员管理 2.1 传递参数roleId=1 2.2 admin/AdminManage.vue 3 用户管理 3.1 传递参数roleId=2 3.2 user/UserManage.vue 管理员管理和用户管理,与之前的Main.vue的内容基本一致,无非是管理…

个人笔记UDP

UDP消息发送发送端​ import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; ​ //不需要连接服务器 public class UdpClientDemo01 {public static void main(String[] args) throws Exception {/…

26届算法秋招_baidu笔试_算法编程题。

给定2个字符串str1、str2&#xff0c;计算把str1转变为str2的最小操作数。可执行的操作有&#xff1a;插入一个字符修改一个字符删除一个字符解题&#xff1a;这是一个经典的编辑距离问题&#xff0c;通常使用动态规划解决。定义dp[i][j]表示将str1的前i个字符转换为str2的前j个…

uniapp-vue3来实现一个金额千分位展示效果

前言&#xff1a;uniapp-vue3来实现一个金额千分位展示效果实现效果&#xff1a;实现目标&#xff1a;1、封装组件&#xff0c;组件内部要实现&#xff0c;input输入金额后&#xff0c;聚焦离开后&#xff0c;金额以千分位效果展示&#xff0c;聚焦后展示大写金额的弹框随时写的…

途游Android面试题及参考答案

对 Java 面向对象的理解是什么?多态的实现方法有哪些? Java 面向对象是一种编程思想,核心在于将现实世界中的事物抽象为 “对象”,每个对象由 “属性”(数据)和 “方法”(行为)组成,通过对象之间的交互完成功能。其核心特性包括封装、继承和多态: 封装是指将对象的属…

通过filezilla在局域网下实现高速传输数据

一. filezilla安装 1.1 linux安装 sudo apt update sudo apt install openssh-server1.2 windows安装 windows安装可以参考这篇文章 二. 使用方法 2.1 wifi下使用方法 直接查看想要连接的电脑的ip&#xff0c;其他的按照有线网络设置好了ip之后进行连接就行。 2.2 有线网…

python的易物小店交换系统

前端开发框架:vue.js 数据库 mysql 版本不限 后端语言框架支持&#xff1a; 1 java(SSM/springboot)-idea/eclipse 2.NodejsVue.js -vscode 3.python(flask/django)–pycharm/vscode 4.php(thinkphp/laravel)-hbuilderx 数据库工具&#xff1a;Navicat/SQLyog等都可以 在需求分…