【C++】类和对象(上篇)——类的定义,访问限定符与this指针

news/2024/5/7 10:55:44/文章来源:https://blog.csdn.net/qq_62306969/article/details/127163872

前言

        C 语言和 C++ 最大的区别就是一个面向过程,一个面向对象。而提到面向对象就不得部提到类,这一篇文章,我们主要探讨一下 C++ 中类的定义以及一些基本的权限。



目录

一、类的引入

二、类的定义

三、访问限定符

3.1 public

3.2 private / protected

四、封装

五、类的大小计算

5.1 类的存储方式

5.2 类的大小的计算方式 

5.3 结构体对齐规则

六、this 指针

6.1 this 指针的引入

6.2 this 指针特性

6.3 this 指针为空的情况


一、类的引入

C 语言结构体中只能定义变量,而在 C++ 中,结构体兼容了之前的 C ,不仅可以定义变量,也可以定义函数。而为了和 C 语言区分开来,C++ 中更喜欢使用 class 来代替 struct

例如:

#include < iostream>
using namespace std;struct Person
{int age;void print(){cout << age << endl;}Person* next;
};int main()
{Person x;x.age = 2;x.print();
}

同时,由于 C++ 中结构体直接作为类名,可以在结构体内直接定义 Person* next,而在 C 语言中则需要用 struct Person* next。

 二、类的定义

例如:

class Person
{int age;
};

class为定义类的关键字,其后跟着的 Person 就是类的名字,{} 中的为类的主体,注意类定义结束时后面分号不能省略

类的主体中内容称为类的成员:

主体中的变量称为 “ 类的属性 ” 或 “ 成员变量 ” ;

主体中的函数称为 “ 类的方法 ” 或 “ 成员函数 ” 。

类通常有两种定义方式:

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

例如:

#include<iostream>
using namespace std;class person
{
public:int age;void add(){age *= 2;}
};
int main()
{person x;x.age = 10;x.add();cout << x.age;return 0;
}

需要注意的是,这种情况下,编译器有概率将函数编译为内联函数,如下:

2. 类声明放在头文件中,成员函数定义放在源文件中,注意:成员函数名前需要加类名 ---  “ :: ”

例如:

#include<iostream>
using namespace std;class person
{
public:int age;void add();
};void person::add()
{age *= 2;
}int main()
{person x;x.age = 10;x.add();cout << x.age;return 0;
}

在类外定义的时候需要注意,应该在函数名前加上 类名 + ::

通常情况下练习可采用第一种,但是更建议采用第二种方式,将类的声明放在头文件中,函数单独放在一个源文件中。

三、访问限定符

3.1 public

公有的意思即是所有人都可以使用,都可以访问,通常是类的函数的定义。

例如:

#include<iostream>
using namespace std;class person
{
public:int age;void add(){age *= 2;}
};
int main()
{person x;x.age = 10;x.add();cout << x.age;return 0;
}

对于变量 age 以及函数 add,我们都可以在类外访问到,而对比下面的 privavte 就能理解什么叫做 “ 公有 ”。 

3.2 private / protected

在初学阶段,private 和 protected 用法基本相同,因此在此不展开。

若 class 类内没有访问限定符,默认为 private 类型,而 struct 类内默认为 public 类型

私有是指在类外无法访问到 private 修饰的类的成员,通常是变量的定义,直接修改变量的函数的定义等。

例如:

#include<iostream>
using namespace std;class person
{
private:int age;
public:void get(){age = 2;}void print(){cout << age;}
};int main()
{person x;// x.age = 1;x.get();// cout << x.age;x.print();return 0;
}

上图中注释行试图修改或者读取 age 的时候,就是对 private 修饰变量的一种访问,这种访问在类外无法进行,只能由类内的函数访问,如 x.print() 就可以访问到 age。

四、封装

我们都知道面向对象有三大特性:封装、继承、多态。今天我们主要谈谈封装

封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来 和对象进行交互。 封装本质上是一种管理,让用户更方便使用类。

通俗来说,封装就是让用户更加规范地使用类,使类内数据更加安全。

例如,我们定义了一个类,其中所有的变量都是 private 限定的,只能通过公有的函数来对变量进行访问,这就是我们对这个类进行封装的实例。

五、类的大小计算

5.1 类的存储方式

对于一个类而言,这个类的函数都是相同的,那么存储时还有必要给每一个类都创建一个新的函数吗?答案当然是否定的。这些类共用同样的一部分函数即可,有人也许就会想到指针,每个类不用创建新的函数,只需要保存到每一个函数的指针即可。

但是实际上 C++ 使用的类的存储方式是,仅保存成员变量的地址,将类成员函数放在另一片公共的区域,需要使用时直接在对应的区域内找即可。

5.2 类的大小的计算方式 

 在计算成员变量的内存空间时,仍旧遵循 C 语言的结构体对齐规则

例如:

#include<iostream>
using namespace std;class A1
{
private:char c;int  x;void show();
public:void print();void add();
};class A2
{
private:void show();
public:void print();void add();
};class A3
{ };int main()
{cout << sizeof A1 << ' ' << sizeof A2 << ' ' << sizeof A3 << endl;return 0;
}

 对于 A1 的内存就是遵循 C 语言的结构体对齐规则,对于 A2 和 A3 这类成员变量占内存大小为空的类而言,类的大小为 1,表示该类存在,所占 1 字节并不存储有效数据。

5.3 结构体对齐规则

1. 第一个成员在与结构体偏移量为0的地址处。

2. 其他成员变量要对齐到对齐数的整数倍的地址处。 注意:对齐数 = 编译器默认对齐数与该成员变量大小的较小值。VS2022中默认的对齐数为8。

3. 结构体总大小为:对齐过程中的最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。

4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

六、this 指针

6.1 this 指针的引入

在上面我们已经知道,对于同一个类型的类而言,成员函数都是在同一块空间,那么编译器是如何识别是哪一个类在调用函数呢?

我们先来看看下面的代码:

#include<iostream>
using namespace std;class student
{
private:string _id;string _name;int _age;
public:void Inset(string id, string name, int age){_id = id, _name = name, _age = age;}void print(){cout << _id << endl << _name << endl << _age << endl;}
};int main()
{student a;student b;a.Inset("001", "大黄", 19);b.Inset("002", "小黄", 18);a.print();b.print();return 0;
}

我们可以看到当 studen 类的 a, b 分别调用 print 函数的时候,打印出来的结果并不相同,可是我们并没有任何参数传入,print 函数又是怎么知道是哪一个类在进行调用的呢?答案就是 this 指针,print 函数实际上隐含了一个 this 指针传参,如下:

void print(student* const _this)
{cout << _this->_id << endl << _this->_name << endl << _this->_age << endl;
}

上图只是一个类似的说法,实际并非完全一样!上图中的 this 指针就是指向当前调用函数的类的一个指针,通过这个指针,函数才能知道应该访问哪一个类的成员变量。需要注意的是,this 指针的定义和传递都是编译器自主实现,用户无法代替编译器定义、传参,但是我们可以对 this 进行使用

6.2 this 指针特性

1. this 指针的类型:类类型* c onst,即成员函数中,不能修改 this 指针本身的指向的地址,即不能给 this 指针赋值,但可修改其所指向的地址存储的内容。

2. this 指针只能在 “ 成员函数 ” 的内部使用。

3. this 指针本质上是 “ 成员函数 ” 的形参,当对象调用成员函数时,将对象地址作为实参传递给 this 形参,所以对象中不存储 this 指针,this 指针存储在栈帧之中,部分编译器会进行优化,存储在寄存器之中。

6.3 this 指针为空的情况

我们先来看看下面两个程序:

#include<iostream>
using namespace std;class student
{
private:string _id;string _name;int _age;
public:void print(){cout << _id << endl << _name << endl << _age << endl;}
};int main()
{student* a = nullptr;a->print();return 0;
}
#include<iostream>
using namespace std;class student
{
private:string _id;string _name;int _age;
public:void print(){cout << "hello world ! " << endl;}
};int main()
{student* a = nullptr;a->print();return 0;
}

大家可以先自己判断一下每一个程序的运行结果。

答案是:第一个程序会运行崩溃,第二个程序则正常运行。

也许大家会觉得奇怪,a 不是空指针吗?为什么还可以写 a->print(),这样不就是对空指针进行访问了吗?

实际上不是,我们已经知道类的成员函数实际上不在类内,而是在一片公共区域,因此 a->print() 本质上没有发生解引用,只是将 a 的地址传参到了 this 指针之中。对于第二个程序,函数调用时,没有对 this 指针进行解引用,因此正常运行,但是第一个程序打印成员变量时,是通过对 this 指针的解应用来进行访问的,因此发生了程序崩溃。

欢迎来和小黄一起学习呀~

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

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

相关文章

PTA - 数据库合集11

目录 10-74 修改学生选课成绩 10-75 添加成绩等级rank字段 10-77 删除成绩为空的学生选课记录 10-74 修改学生选课成绩 分数 5 全屏浏览题目 切换布局 作者 张庆 单位 集美大学 本题目要求编写UPDATE语句&#xff0c; 在SC表中修改‘C001’课程的成绩&#xff0c;若成绩小…

【自学】利用python进行数据分析 LESSON6 <pandas入门——pandas数据结构介绍2>

目录 前言 一、DataFrame 1. 列的选取 2. 行的选取 3. 列的修改 4. 列的删除 5. 嵌套字典赋给DataFrame 总结 前言 继续上一节的内容。往期内容如下&#xff1a; 【自学】利用python进行数据分析 LESSON5 &#xff1c;pandas入门——pandas数据结构介绍1&#xff1e;_…

数据结构初阶 顺序表补充

一. 题目的要求 写出三种链表的接口函数 它们的功能分别是 1 查找数的位置 2 在pos位置插入值 3 在pos位置删除值 二. 实现pos 这个其实很简单 找到一步步遍历 找到这个数字就返回 找不到就提示用户下 这 个数字不存在 int SeqListFind(SL* ps,int x) {int i;for ( i …

Red Hat Enterprise Linux release 8.0 (Ootpa)-性能调优工具Tuned

一、Tuned简介Tuned是Red Hat Linux操作系统自带的性能调优工具,通过针对特定应用场景提供配置来改善系统性能,自Red Hat Enterprise Linux/CentOS的6.3版本开始出现,包括两部分tuned和tuned-adm,其中tuned是服务端程序,用来监控和收集系统各个组件的数据,并依据数据提供…

SpringBoot二十六课大纲和目录

目录 ​​ 即使是在憎恨和杀戮中&#xff0c;仍然有些东西值得人们为之活下去。一次美丽的相遇&#xff0c;或是为了美丽事物的存在。我们描绘憎恨&#xff0c;是为了描写更重要的东西。我们描绘诅咒&#xff0c;是为了描写解放后的喜悦。 SpringBoot《第一课》_星辰镜的博客…

clickhouse常见部署图及LowCardinality低基数类型优缺点

clickhouse使用jdbc进行查询插入数据操作的部署图 一.读sql的核心是 读分布式表的数据&#xff08;分布式表可以读取互为副本的本地表的数据&#xff0c;起到容灾的目的&#xff09;&#xff0c;然后使用nginx作为负载均衡器和反向代理&#xff0c;代理后端clickhouse的分布式…

什么是低代码

文章目录no code / low code / pro code按适用范围的维度来分类低代码的技术意义与商业价值技术意义商业价值行业状态速读平台分类不同的实现方式不同的使用群体不同的使用方式优秀开源项目推荐no code / low code / pro code no code&#xff1a;自己编程给自己用&#xff0c…

Yao‘s GC 的通信最优解:Half Gate

参考文献&#xff1a; Bellare M, Hoang V T, Rogaway P. Foundations of garbled circuits[C]//Proceedings of the 2012 ACM conference on Computer and communications security. 2012: 784-796.Zahur S, Rosulek M, Evans D. Two halves make a whole[C]//Annual Interna…

MyBatisPlus入门宝典(二)CRUD

目录 一.添加 二.相关注解 三.修改 四.删除 五.查询 六.条件构造器 七.分页查询 八.全局配置 一.添加 1.配置文件开启SQL日志打印 # 开启SQL日志 mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 2.测试添加方法&#xff1a; …

Unity URP 色彩之旅

Unity URP 色彩之旅 这一切只是色彩科学的冰山一角… 文章目录Unity URP 色彩之旅1 我们是如何感知世界的&#xff1f;1.1 首先要有光&#xff01;1.2 人眼响应1.3 奇怪的大脑2 我们是如何描述颜色的&#xff1f;2.1 CIE 1931 RGB Color Space2.2 CIE 1931 XYZ Color Space2.3 …

JavaScript高级学习笔记:数据_变量_内存

1. 什么是数据? 2. 什么是内存? 3. 什么是变量? 4. 内存,数据, 变量三者之间的关系 变量保存的是内存中存储的地址值&#xff0c;而变量赋值就是将一个变量保存的内容拷贝一份到另一个变量中 这里面的.就是找obj对应地址值&#xff0c;中内存保存的相应数据 那么是不是所有…

SRv6----报文转发流程

按照下图路径&#xff0c;报文需要从主机H1转发到主机H2,H1将报文发送给节点A处理。节点A、B、D和F均为支持SRv6的设备&#xff0c;节点C和节点E为不支持SRv6的设备。 我们在SRv6源节点A上进行了网络编程&#xff0c;希望报文经过B-C和D-E这两条链路&#xff0c;然后送达节点F&…

华为面向5G的室内覆盖数字化概述

概述 数字化技术催生各行业的不断创新&#xff1a;ICT、媒体、金融、保险在数字化发展 曲线中已经独占鳌头&#xff0c;零售、汽车、油气化工、健康、矿业、农业等也在加速 其进程。促进数字化进程的关键技术包括软件定义设备、大数据、云计算、区 块链、网络安全、时延敏感网…

(附源码)SSM医疗垃圾管理系统JAVA计算机毕业设计项目

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

美团java一面面经

目录1.了解static吗&#xff0c;static数据存在哪&#xff1f;生命周期什么样的2.了解final吗&#xff0c;讲讲下面这段代码的结果3.讲讲volatile吧4.讲讲两个锁的区别(reentrantlock和synchronized)5.讲讲线程池里线程的创建与销毁&#xff0c;核心线程可以销毁吗&#xff1f;…

.NET 开源项目推荐之 直播控制台解决方案 Macro Deck

在直播圈有个很受欢迎的直播控制台程序Macro Deck, 它是基于Apache 2.0协议开源的.NET 应用。流媒体是一个吸引数亿万玩家的严肃行业。 最受欢迎的游戏锦标赛的转播获得了数百万的观看次数,从商业角度来看,这也使游戏行业变得有趣。在直播圈有个很受欢迎的直播控制台程序Mac…

牛客网专项练习30天Pytnon篇第07天

1.在Python中&#xff0c;使用open方法打开文件,语法如下&#xff1a;open(文件名&#xff0c;访问模式)&#xff0c;如果以二进制格式打开一个文件用于追加&#xff0c;则访问模式为:&#xff08;C&#xff09; A.rb B.wb C.ab D.a 解析&#xff1a; "r",&q…

看完这篇 教你玩转渗透测试靶机vulnhub——hackableII

Vulnhub靶机hackableII渗透测试详解Vulnhub靶机介绍&#xff1a;Vulnhub靶机下载&#xff1a;Vulnhub靶机安装&#xff1a;Vulnhub靶机漏洞详解&#xff1a;①&#xff1a;信息收集&#xff1a;②&#xff1a;FTP匿名登录&#xff1a;③&#xff1a;回弹shell&#xff1a;④&am…

Mybatis - 一二级缓存的原理

Mybatis - 一二级缓存的原理前言一. 一级缓存原理1.1 原理分析1.2 一级缓存 Key1.3 查询逻辑1.4 一级缓存的清除或失效场景1.5 一级缓存总结二. 二级缓存原理2.1 二级缓存的实验2.2 二级缓存的开启和相关配置解析2.3 二级缓存的封装Cache类2.4 二级缓存的存储2.5 二级缓存总结前…

指静脉代码学习---9.图像质量评价(分类)

一、论文背景 1.论文三--Song 本文提出了一种自适应增强框架的算法流程 先通过质量评价将图像分类,①针对高质量的图像,采用类似直方图均衡化的简单方法②低质量图像,采用类似滤波器增强的方法(虽然时效性较差,但是效果比较明显) ①对质量评价方法历程的概述: