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

C++:类和对象(上)

类的概念及定义

                类的概念:

        类(Class)是C++中面向对象编程的基础概念之一,它定义了一种数据类型,是一种用户自定义的数据结构。类可以看作是对象的蓝图或模板,描述了对象具有的属性(数据成员)和行成员函数)

                        

        如上图 外卖平台就是一个模板,而C语言更注重过程,考虑的是一个订单从出餐到送餐的过程,而C++除了注重过程还更加注重还关注整个外卖服务的各个方面。在C++中,你会把订单看作一个对象,这个对象不仅包含了订单的数据还包含了一系列与订单相关的行为。这些行为(或者说函数)与订单对象紧密相关,被封装在订单对象内部。

        C++则更偏向于以对象为中心,通过类和对象来组织代码,并且强调数据和行为的封装与抽象化

                类定义的格式:

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

        

#pragma once
#include<iostream>
#include<stdlib.h>
class Stack
{void STInit(int capaticy = 4){a = (int*)malloc(sizeof(int) * capaticy);_top = 0;_capaticy = capaticy;}void STPush(int x){a[_top++] = x;}void Destroy(){free(a);a = nullptr;}int* a;int _top;int _capaticy;
};

        如上图这段代码是一个用C++编写的简单的栈(Stack)类的实现。可以看出和C语言中的栈的结构体类似但又有很大的区别。

        在类里不仅可以定义成员变量,更可以定义成员函数(这点在C语言上是不允许的)。

        可以看到通过使用类对栈进行初始化以及进行插入时的函数调用就会简洁很多,如果是C语言那么则还需要传地址会相对麻烦。

                类的访问限定符:

在C++中,访问限定符用于控制类(或结构体)的成员变量和成员函数对外部代码的可见性和访问权限。主要的访问限定符包括public(公共)、protected(保护)和private(私人)。

        public中文名为公共,顾名思义是所有人都可以用的。修饰的成员在类外可以直接被访问;protected和privated都具有隐私性,修饰的成员在类外不能直接被访问,protected和private是⼀样的,以后往后才能体现出他们的区别。
        
class Stack
{
public:void STInit(int capaticy = 4);void STPush(int x);void Destroy();	private:int* a;int _top;int _capaticy;
};

        上图小编将Stack类里的成员函数权限设定为public,将成员变量权限设置为私人。可以看出主函数中调用类里的成员函数是没有任何问题的,而一旦想要直接调用类里的成员变量则会发生编译错误,因为私人区域是不允许直接调用。

        上图的将成员变量和成员函数捆绑在一起,并加以保护,是为了防止外部直接访问和修改对象的内部状态。封装有助于隐藏对象的内部细节,同时提供了一个清晰定义的接口,使得程序员无需关心对象内部的实现细节,只需要通过公共接口与对象进行交互即可,这种就是类的第一特性:封装。

        封装的本质是⼀种更严格规范的管理,避免出现乱访问修改的导致类的属性被破坏的问题。

                类域:

        类同样拥有自己的域,类的所有成员都在类的作⽤域中,同时类域不影响变量⽣命周期。类域影响的是编译的查找规则,所以在类体外定义成员时需要使⽤ :: 作⽤域操作符指明成员属于哪个类域。
        
        上图除了主文件,再次添加了两个文件一个头文件Fun.h与Fun.cpp。
        将类的声明放到头文件中,并将成员函数的定义放在Fun.cpp中,可以看到要在Fun.cpp中定义类中成员函数需要通过 : :(域作用限定符)编译器才能查找的声明在哪⾥,否则就会报错。

实体化

        实体化概念:

        ⽤类类型在物理内存中创建对象的过程,称为类实例化出对象。
        类是对象进⾏⼀种抽象描述,是⼀个模型⼀样的东西,限定了类有哪些成员变量,这些成员变量只 是声明,没有分配空间,⽤类实例化出对象时,才会分配空间。
class Data
{
public:void Init(int year,int month,int data){_year = year;_month = month;_data = data;}void Print(){cout <<  _year << "-" << _month << "-" << _data << endl;}private://变量只是声明并没有实际的开辟空间int _year;int _month;int _data;
};int main()
{//实体化出对象d1与d2Data d1, d2;d1.Init(2022, 11, 11);d2.Init(2022, 12, 12);d1.Print();d2.Print();}

        从上图可以看到,如果直接给Data类里的_data直接进行赋值会报错,而实体化出的对象d1则不会。这就类似于建房子,你不可能在图纸样板里面建房子,只能在实际的地面上才能。

        

                

        关于实体化里的对象大小:

                在了解实体化里对象大小的概念前需要了解结构体的内存对齐以及内存对齐的规则,如果有不清楚的可以看看小编之前写的一篇文章C语言结构体知识-CSDN博客。

        

        从上图可以看出使用sizeof计算出Data类与对象d1的大小结果都是12,那上面又说成员变量只是声明没有分配空间。为什么sizeof还能计算出类的大小呢?其实要想明白也很简单,就好房子的图纸上都有标注空间长度大小,而d1又类似于实际去房子里丈量,两种方法都可以得知房子的尺寸。

        而通过sizeof发现,类的大小为12,刚好三个成员变量int的值。那么成员函数难道没有参与计算吗?

        此时将代码进行调试并转到反汇编指令时发现,d1.Init()与 d2.Init()都call向同一块空间,那么也就说明d1的成员函数与d2的成员函数是共用的,所以类成员函数的是存放在一个公共的代码区域段的地方,那么这样做也是为了节约空间。

        就好比对象1与对象2相约一起打篮球,而打篮球需要篮球场。篮球场可以建在对象1的家里同样也可以建在对象2的家里但这样做都会很浪费空间。那么将篮球场建立在一个公共的区域,既方便调用又节约了对象1与对象2自身的空间。

        

        通过上图运行代码,我们可以看到A1类既没成员函数也没成员变量,A2类只有一个成员函数。A1类的对象a与A2类的对象b通过sizeof计算出它们的大小都为1,这是为了确保每个对象在内存中占有独立的空间,即使是空类也需要有独特的地址。因为如果⼀个字节都不给,怎么表⽰对象存在过呢!

this指针:

        

        通过上图可以看到将Data类的d1与d2对象进行了初始化,并且输出他们的成员变量。但根据上面的知识我们可以得知,类成员函数是存在于公共代码段里的,而Print函数又没有参数但又却能准确的显示d1与d2对象中的参数,那编译器是怎么知道是调用d1还是d2呢?

        

        通过观察反汇编指令可以发现,编译器在调用Print函数之前会先将d1的地址与d2的地址压入ecx寄存器中然后再调用函数,但我们自己并没有去手动的将d1与d2的地址进行传参,那么这里就涉及到C++中的this指针。

        编译器编译后,类的成员函数默认都会在形参第⼀个位置,增加⼀个当前类类型的指针,叫做this指针。⽐如Date类的Print的真实原型为void Print( Data* this),而类的成员函数中访问成员变量,本质都是通过this指针访问的也就是 cout << this-> _year << "-" << this->_month << "-" << this->_data << endl;

        C++规定不能在实参和形参的位置显示的写this指针(编译时编译器会处理),但是可以在函数体内显示使用this指针。

 

        

        

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • sysinternals工具包
  • 《Nginx核心技术》第08章:为Nginx动态添加模块
  • 第6章>>实验7:PS(ARM)端Linux RT与PL端FPGA之间(通过Memory存储器进行通信和交互)《LabVIEW ZYNQ FPGA宝典》
  • Android SurfaceFlinger——信号同步原理(五十一)
  • 探索WebKit的奥秘:塑造高效、兼容的现代网页应用
  • maxscript循环中提高性能
  • 记一次对加密后pythonEXP的解密以及分析
  • SS9283403 sqlite3交叉编译并部署到SS928(六)
  • Together规则引擎 金融解决方案
  • 八股文”在实际工作中的作用:敲门砖还是空谈?
  • supermap制作发布二三维地图服务
  • 基于企业微信第三方接口开发,发送朋友圈评论
  • MySQL:数据库权限与角色
  • 单机系统怎么做高可用设计
  • 【强化学习】强化学习的基本概念与应用
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • 345-反转字符串中的元音字母
  • 4个实用的微服务测试策略
  • Debian下无root权限使用Python访问Oracle
  • HTTP那些事
  • IDEA 插件开发入门教程
  • java B2B2C 源码多租户电子商城系统-Kafka基本使用介绍
  • JDK9: 集成 Jshell 和 Maven 项目.
  • MySQL QA
  • mysql 数据库四种事务隔离级别
  • node.js
  • Phpstorm怎样批量删除空行?
  • Spring Cloud中负载均衡器概览
  • 分类模型——Logistics Regression
  • 区块链分支循环
  • 深入 Nginx 之配置篇
  • 双管齐下,VMware的容器新战略
  • 微信小程序--------语音识别(前端自己也能玩)
  • 运行时添加log4j2的appender
  • 正则表达式
  • # Redis 入门到精通(八)-- 服务器配置-redis.conf配置与高级数据类型
  • # 利刃出鞘_Tomcat 核心原理解析(二)
  • #07【面试问题整理】嵌入式软件工程师
  • (9)YOLO-Pose:使用对象关键点相似性损失增强多人姿态估计的增强版YOLO
  • (Forward) Music Player: From UI Proposal to Code
  • (超详细)语音信号处理之特征提取
  • (非本人原创)我们工作到底是为了什么?​——HP大中华区总裁孙振耀退休感言(r4笔记第60天)...
  • (剑指Offer)面试题34:丑数
  • (六)库存超卖案例实战——使用mysql分布式锁解决“超卖”问题
  • (篇九)MySQL常用内置函数
  • (三)docker:Dockerfile构建容器运行jar包
  • (四)事件系统
  • (一)eclipse Dynamic web project 工程目录以及文件路径问题
  • (译) 函数式 JS #1:简介
  • (原)本想说脏话,奈何已放下
  • (终章)[图像识别]13.OpenCV案例 自定义训练集分类器物体检测
  • (转)http-server应用
  • .bat批处理(五):遍历指定目录下资源文件并更新
  • .equal()和==的区别 怎样判断字符串为空问题: Illegal invoke-super to void nio.file.AccessDeniedException
  • .htaccess 强制https 单独排除某个目录