当前位置: 首页 > news >正文

C++入门:类和对象(上)

类和对象重点解析

  • 1.类的定义
    • 1.类的访问限定符及封装
      • 1.C++实现封装的方式
      • 2.访问限定符
        • 注意
      • 3.封装
  • 2.类对象模型
    • 2.1类对象存储方式
    • 2.2类对象的大小
      • 2.2.1结构体内存对齐原则
      • 2.2.2为什么要内存对齐
  • 3.this指针
    • 3.1this指针的引出
    • 3.2this指针的特性
    • 3.3this指针的存储
    • 3.4this指针可以为空吗?

1.类的定义

1.类的访问限定符及封装

1.C++实现封装的方式

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

2.访问限定符

public(公有),protect(保护),private(私有)

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

3.封装

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

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

封装本质上是一种管理,让用户更方便使用类。
通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用。

2.类对象模型

2.1类对象存储方式

我们知道在C++中类的实现只是一个模板,用类创建成员后才会分配空间。如果创建多个对象,而每一个成员的函数都是一样的,那么在创建变量时是每一个成员都会有类函数还是所有的成员共用一个函数呢?那么我们在计算一个类对象的大小时应不应该计算这些函数的大小呢?

存储方式:每个对象中只保存成员变量,成员函数存放在公共的代码段。

a5468091abde0dd8a86df0.png)

2.2类对象的大小

2.2.1结构体内存对齐原则

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

注意:没有成员变量的类默认是1字节,占位标识对象的存在

2.2.2为什么要内存对齐

class A
{
private:char c;int i;
}

假设有一个类,先表示出对齐和不对齐的内存中的存储形式。
在这里插入图片描述
假设有三种读取方式:
方式一(内存对齐时):按照对齐的方式读取:先读取char,然后丢弃三个二进制位,再直接从int的开始位置进行读取。
方式二(不内存对齐时):先读取char,然后从char的下一个位置,也就是和char连续存储的int的位置。
方式三:先读取char,然后将剩余的三个字节存起来,再读取int的剩下的字节,然后将这两部分拼接起来。

首先,计算机根据地址线的大小,来确定能给多少存储器进行编码,是按照整数倍来读取,而不是从任意位置进行读取。而如果选择读取多次再进行数据的拼接的话更加麻烦,相比之下,内存对齐时只需要按顺序读取再丢掉不需要的那部分即可。
即:内存对齐就是计算机在读取数据的时候,尽量可以一次取到完整的数据而不是要分很多次才能拿到完整的数据,而且分多次拿到的话还需要进行数据的拼接组合,这样就会更加麻烦了。

3.this指针

3.1this指针的引出

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, d2;d1.Init(2022,1,11);d2.Init(2022, 1, 12);d1.Print();d2.Print();return 0;
}

对于上面的Date类当我们在调用类的成员函数中,由于没有对于对象的区分,那么编译器是如何知道是哪个对象调用了它的函数从而执行对应的操作呢?
比如在打印时如何区分打印d1的信息还是打印d2的信息。

解决方案:

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

3.2this指针的特性

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

3.3this指针的存储

不存储在堆上:堆上内存需要我们自己去申请。
不存储在对象内:我们在计算类对象大小时并没有将其算入在内。
不存储在常量区和静态区:没有static修饰,其也不是常量。

this指针是地址,它的作用为作为形参,其存储在栈空间上,但是有的编译器比如VS可能会存储在寄存器中,这样访问的时候比较快。

3.4this指针可以为空吗?

可以。在我们之前的理解中,认为所有的空指针配合上解引用操作符都会崩毁,但是在一些情况下并非如此。

观察下面两段代码:
代码一:

class A
{
public:void Print(){cout << "Print()" << endl;}
private:int _a;
};
int main()
{A* p = nullptr;(*p).PrintA();p->Print();return 0;
}
这段代码可以正确打印出结果,程序不会崩溃。

代码二:

class A
{
public:void PrintA(){cout << _a << endl;}
private:int _a;
};
int main()
{A* p = nullptr;(*p).PrintA();p->PrintA();return 0;
}
这里仅仅和上面有打印函数的差别,但是程序会崩溃。

首先,有*或者->都不一定是解引用,解引用以后要有实际意义编译器才会解引用。
我们上面的(*p)和->本质是给成员函数传递this指针,确定是哪个对象调用的this指针,this指针作为形参传递过去。

代码一:我们只是将空的this指针传递过去,在函数内部并没有解引用,因此程序正常运行。

代码二:我们将空的this指针传递之后,由于需要打印成员变量,因此必须对于this指针解引用从而找到该变量,进而造成空指针的解引用。

汇编观察:
在这里插入图片描述

我们通过反汇编也可以观察到,在底层并没有先对指针解引用,而是通过p去调用对应的Print函数。

以上是本次所有内容,谢谢观看。

相关文章:

  • 车辆信息查询API:高效获取车牌号对应车辆的实时信息
  • 从0写一个问卷调查APP的第13天-1
  • MySQL基础复习
  • Python安装手册
  • 【课程】Java构架师42套阶段课程
  • vscode集成git管理项目
  • ensp ppp验证实验(二)
  • 低代码开发平台开源:依靠科技力量实现数字化转型!
  • Guava之EventBus源码分析
  • 从0到1:Java构建高并发、高可用分布式系统的实战经验分享
  • G - Find a way
  • RUST:Arc (Atomic Reference Counted) 原子引用计数
  • 面试笔记——Redis(双写一致、持久化)
  • 11.创建后台系统项目
  • 鸿蒙Harmony应用开发—ArkTS-if/else:条件渲染
  • 【面试系列】之二:关于js原型
  • 10个最佳ES6特性 ES7与ES8的特性
  • CAP 一致性协议及应用解析
  • ES6系列(二)变量的解构赋值
  • export和import的用法总结
  • SpiderData 2019年2月23日 DApp数据排行榜
  • Vue组件定义
  • 案例分享〡三拾众筹持续交付开发流程支撑创新业务
  • 对象管理器(defineProperty)学习笔记
  • 基于Javascript, Springboot的管理系统报表查询页面代码设计
  • 两列自适应布局方案整理
  • 算法-插入排序
  • CMake 入门1/5:基于阿里云 ECS搭建体验环境
  • Java性能优化之JVM GC(垃圾回收机制)
  • #stm32驱动外设模块总结w5500模块
  • (1)常见O(n^2)排序算法解析
  • (C语言)fgets与fputs函数详解
  • (C语言)编写程序将一个4×4的数组进行顺时针旋转90度后输出。
  • (Demo分享)利用原生JavaScript-随机数-实现做一个烟花案例
  • (层次遍历)104. 二叉树的最大深度
  • (第二周)效能测试
  • (二)【Jmeter】专栏实战项目靶场drupal部署
  • (附源码)python旅游推荐系统 毕业设计 250623
  • (附源码)springboot 个人网页的网站 毕业设计031623
  • (免费领源码)python#django#mysql校园校园宿舍管理系统84831-计算机毕业设计项目选题推荐
  • (免费领源码)python+django+mysql线上兼职平台系统83320-计算机毕业设计项目选题推荐
  • (一)插入排序
  • (原创)攻击方式学习之(4) - 拒绝服务(DOS/DDOS/DRDOS)
  • (原創) 如何安裝Linux版本的Quartus II? (SOC) (Quartus II) (Linux) (RedHat) (VirtualBox)
  • ****** 二十三 ******、软设笔记【数据库】-数据操作-常用关系操作、关系运算
  • .NET 6 Mysql Canal (CDC 增量同步,捕获变更数据) 案例版
  • .NET Micro Framework初体验(二)
  • .net 调用php,php 调用.net com组件 --
  • .net 后台导出excel ,word
  • .Net高阶异常处理第二篇~~ dump进阶之MiniDumpWriter
  • .NET开源快速、强大、免费的电子表格组件
  • .NET序列化 serializable,反序列化
  • .ui文件相关
  • /proc/stat文件详解(翻译)
  • [ C++ ] STL---string类的使用指南