C++:类和对象(上)---初步认识类和this指针

news2024/11/27 16:52:18

文章目录

  • 面向过程和面向对象
  • 类的引入
  • 类的定义
    • 类定义的方式
  • 类的访问限定符
  • 类的实例化
  • 类对象模型
    • 类对象的大小
  • this指针

面向过程和面向对象

C语言是一门面向过程的语言关注的是过程,确认一个问题求解的步骤,再一步一步对它进行解决

C++是一门基于面向对象的语言,它更关注的是对象,将一个事情分成不同的对象,再用对象完成问题的解决

类的引入

在C语言中有结构体,但结构体中只能定义变量,而在C++中还可以定义函数,例如,数据结构中的链表栈队列等函数都需要定义成员,在外部定义函数,通过函数改变具体数据结构的内容,而在C++中,结构体中也是可以定义函数的,这是C++对C的提升

类的定义

class className
{
// 类体:由成员函数和成员变量组成
};  // 一定要注意后面的分号
  1. class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。

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

类定义的方式

  1. 声明和定义都放在类体中,其中需要注意的是,如果成员函数在类内定义,那么编译器可能会把成员函数当作内联函数来处理

具体定义形式如下:

#include <iostream>
using namespace std;

class Time
{
public:

	void settime()
	{
		cin >> hour >> minute >> second;
	}

	void show()
	{
		cout << hour << endl;
		cout << minute << endl;
		cout << second << endl;
	}

private:
	int hour;
	int minute;
	int second;
};

int main()
{
	Time t1;
	t1.settime();
	t1.show();
	return 0;
}
  1. 类声明放在.h文件中,定义放在.cpp文件中,一般来说更希望使用这种写法

在这里插入图片描述

类的访问限定符

关于访问限定符:

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

类的实例化

通常来说,用类的类型创建出对象的过程就是类的实例化

我们可以这样理解,类就是一张设计图纸,而把设计图纸变现才能有实际空间,占用内存空间,因此,类本身是没有空间的,只有把类实例化出一个对象才有实际的空间

类对象模型

类对象的大小

既然创建出了一个类,那么这个类的对象中包含了什么,一个类的大小又如何计算

假设我们有下面的代码

#include <iostream>
using namespace std;

class date
{
public:
	void print()
	{
		cout << "void print()" << a << endl;
	}
private:
	int a;
};

int main()
{
	date d1;
	cout << sizeof(date) << endl;
	cout << sizeof(d1) << endl;
	cout << &d1 << endl;
}

在这里插入图片描述
那么类的大小究竟是如何计算的?下面就进行类的存储方式的介绍

在C++中,对象的存储方式是这样的:

在这里插入图片描述

因此,类中的成员是独立存在的,而类的成员函数是放在公共代码区的

类的成员是可以独立相加的,它的规则其实和结构体的计算相同,都是遵循内存对齐规则进行相加

而公共代码区的证明也可以通过汇编来看

在这里插入图片描述
从中可以看出,类的成员函数在调用的时候确实是在一块固定的公共代码区进行的调用,因此就理解了类在计算机内部是如何存储的

但依旧有两个特例:

// 类中仅有成员函数
class A 
{
public:
	void f2() {}
};

// 类中什么都没有---空类
class B
{};

int main()
{
	cout << sizeof(A) << endl;
	cout << sizeof(B) << endl;
}

当类中没有成员或直接就是空类的时候,它们的大小均为1,这个1没有实际意义,只是用来占位,证明这里曾经存在过A和B

this指针

C++中还引入了this指针的概念,这是一个较为隐秘的内容

this指针引入的原因

当我们创建好类,要用类定义变量时,再利用变量调用公共代码区的成员函数时,你是否有这样的问题:函数体中并没有写到不同对象的区分,那么不同的变量调用函数时,函数是如何确定应该作用于哪个变量中?
这就是this指针的功劳

C++中引入了this指针,这个this指针是每一个非静态的成员函数的指针参数,也就是说,每一个成员函数的实际参数要比它表面的参数多一个指针参数,而这个指针存在的意义就是在调用时指向当前对象,这个操作是编译器自动完成,整个过程是透明的,用户不需要完成这些操作

下面代码和图解来解释一下:
现在有这样的代码:

#include <iostream>
using namespace std;
class date
{
public:
	void print(int year)
	{
		a = year;
	}
private:
	int a;
};

int main()
{
	date d1;
	date d2;
	d1.print(2004);
	d2.print(2003);
}

表面上看,print函数的参数只有一个year,d1和d2调用print函数的时候传递的也只有一个参数,但实际上在编译器内部是这样的

在这里插入图片描述

this指针的特性

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

关于理解this指针

// 下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
	void Print()
	{
		cout << "Print()" << endl;
	}
private:
	int _a;
};
int main()
{
	A* p = nullptr;
	p->Print();
	return 0;
}

编译运行可以正常运行,原因就在于上面讲的成员函数存储位置的问题

本题的问题点在于,这里p是空指针,我直接解引用空指针难道不是错误的吗?
其实不然,这里根本没有解引用,首先要清楚,解引用的意思是我要用一个指针去访问这块空间区域内的某个内容,这个过程是解引用,而Print函数是存在对象里面的吗?很明显并不是,成员函数是存储在公共代码区的,这里看似解引用实际上只是普通的函数调用,通过汇编也可以很好的解释这个现象

这里我们把private注释掉,使得指针可以访问对象内部的成员_a

在这里插入图片描述
最下方的汇编才是解引用,我们要访问对象内部的数据_a,当然空指针是不可以解引用的…
但重点更好的说明了,上面的语句只是单纯调用了Print函数而已,第一句是把this指针传递给了函数,也就是所谓的传参,第二句call则是所谓的调用,执行函数的本质就是传参和调用,从汇编上可以更好的理解这个问题

如果你对这里还是有所疑问,再来看下面的例子对比

class Date
{
public:
	void Print()
	{
		cout << this << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	d1.Print();

	Date* p2 = nullptr;
	p2->Print();

	return 0;
}

在这里插入图片描述

上面的代码意思是把调用Print函数打印this指针的值,这个this指针其实就是对象的地址,这个代码可以更好的解释我们前面所说明的,成员函数的调用就是传递了this指针参数后进行的函数调用

如果理解了上面的题目,下面还有一道类似的题:

class A
{
public:
	void PrintA()
	{
		cout << _a << endl;
	}
private:
	int _a;
};
int main()
{
	A* p = nullptr;
	p->PrintA();
	return 0;
}

如果你理解了上面的题目,那么下面的题就很简单了,这里唯一的区别是Print函数中要打印成员的值,而实际上打印的是this->_a,因此,传递参数的this是nullptr,所以程序会崩溃

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

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

相关文章

【学会动态规划】最小路径和(9)

目录 动态规划怎么学&#xff1f; 1. 题目解析 2. 算法原理 1. 状态表示 2. 状态转移方程 3. 初始化 4. 填表顺序 5. 返回值 3. 代码编写 写在最后&#xff1a; 动态规划怎么学&#xff1f; 学习一个算法没有捷径&#xff0c;更何况是学习动态规划&#xff0c; 跟我…

Kotlin 协程基础入门:Channel(通道)

简介 Kotlin 中的 Channel&#xff08;通道&#xff09;是一种协程间通信的机制&#xff0c;用于在协程之间传递数据。它提供了一种有序、线程安全的方式&#xff0c;让生产者和消费者之间进行异步数据传输。 Channel 这个管道的其中一端&#xff0c;是发送方&#xff1b;管道…

OpenCV实现照片换底色处理

目录 1.导言 2.引言 3.代码分析 4.优化改进 5.总结 1.导言 在图像处理领域&#xff0c;OpenCV是一款强大而广泛应用的开源库&#xff0c;能够提供丰富的图像处理和计算机视觉功能。本篇博客将介绍如何利用Qt 编辑器调用OpenCV库对照片进行换底色处理&#xff0c;实现更加…

Promise 讲解,js知识,es6

文章目录 一、Promise的三种状态1. 初始态pending2. 成功态fulfilled&#xff0c;调用resolve方法3. 失败态rejected&#xff0c;调用reject方法 二、Promise的方法then方法catch方法 三、async和awaitasync 函数await 表达式 四、代码举例帮助理解1、Promise的值通过then方法获…

【idea】编译热部署

项目场景&#xff1a; 实际工作中&#xff0c;用到了idea&#xff0c;发现idea不编译代码&#xff0c;热部署什么的都不行 问题描述 在实际的工作中idea遇到了各种问题&#xff0c;之前一直用的2022版的&#xff0c;公司用的jboss起的项目&#xff0c;启动过程极其痛苦&#…

电子档案管理系统

电子文档 登陆成功后点击左上角“”选择“档案管理”跳转到“档案管理首页”如下图: 该界面列出用户被授权查看的可视化数据图形,柱图、饼图、线图、雷达图等,并结合数据仓库里的动态数据进行数据展现。 图形所展示的数据可根据企业需求定制,点击图形即可查看关联内容,方…

C++类与对象(上部曲)

目录 面向过程和面向对象初步认识 类的引入 类的定义 类的两种定义方式&#xff1a; 1. 声明和定义全部放在类体中 2. 类声明放在.h文件中&#xff0c;成员函数定义放在.cpp文件中 类的访问限定符及封装 1 访问限定符 2 封装 类的实例化 类对象的存储方式 this指针 …

IDEA快速创建SpringBoot

文件具有错误的版本 61.0, 应为 52.0报错可以看看是不是Springboot的版本比较高 和jdk版本不匹配 package com.qf.controller;import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframewor…

黑马 pink h5+css3+移动端前端

网页概念 网页是网站的一页,网页有很多元素组成,包括视频图片文字视频链接等等,以.htm和.html后缀结尾,俗称html文件 HTML 超文本标记语言,描述网页语言,不是编程语言,是标记语言,有标签组成 超文本指的是不光文本,还有图片视频等等标签 常用浏览器 firefox google safari…

从Vue2到Vue3【四】——Composition API(第四章)

系列文章目录 内容链接从Vue2到Vue3【零】Vue3简介从Vue2到Vue3【一】Composition API&#xff08;第一章&#xff09;从Vue2到Vue3【二】Composition API&#xff08;第二章&#xff09;从Vue2到Vue3【三】Composition API&#xff08;第三章&#xff09;从Vue2到Vue3【四】C…

os.environ[“CUDA_VISIBLE_DEVICES“]学习总结

今天发现一个很有意思的东西 import torch import os # Specify the GPU device os.environ["CUDA_VISIBLE_DEVICES"] "1" print(torch.cuda.is_available())但是如果修改下面的设置后&#xff0c;结果就变成了 import torch import os # Specify the…

【100天精通python】Day7:数据结构_列表 List的创建、删除、访问、遍历、统计、排序、推导等使用

目录 1 列表的创建 2 列表的删除 3 访问列表元素 4 遍历列表 5 添加修改删除列表元素 6 对列表进行统计和计算 7 对列表进行排序 8 列表推导式 9 多维列表 在Python中&#xff0c;列表是一种有序的可变数据类型&#xff0c;用于存储一组元素。 列表使用方括号“[] ”来…

汇编习题1-100和

.text .globl _start_start:mov r0,#0MOV r1,#0stop:cmp r1,#0x64addcc r1,r1,#0x1addcc r0,r0,r1b stop .end运行结果&#xff1a; 寄存器R0就为16进制的结果

K8S初级入门系列之四-Namespace/ConfigMap/Secret

一、前言 本章节我们继续学习Namespace、ConfigMap、Secret基础概念&#xff0c;了解他们基本用法和操作。NameSpace为命名空间&#xff0c;在同一集群中试下资源隔离。ConfigMap通过key-value的方式实现明文配置数据的保存&#xff0c;Secret与ConfigMap类似&#xff0c;不过是…

Windows下使用rocketMq

1、下载&#xff08;下载zip后解压即可&#xff09; 下载地址&#xff1a;下载 | RocketMQ 2、配置环境变量&#xff08;注意&#xff1a;该目录的下一级是bin&#xff09; 3、启动 在bin目录下使用cmd 分别输入 3.1 启动name server (下图是启动成功的显示&#xff0c;窗口…

CPU密集型和IO密集型任务的权衡:如何找到最佳平衡点

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、概览三、CPU密集型与IO密集型3.1、CPU密集型3.2、I/O密…

dp算法 力扣978、力扣139、力扣467

目录 一、力扣978978. 最长湍流子数组 - 力扣&#xff08;LeetCode&#xff09; &#xff08;一&#xff09;题目详情 &#xff08;二&#xff09;算法讲解 &#xff08;三&#xff09;代码 二、力扣139139. 单词拆分 - 力扣&#xff08;LeetCode&#xff09; &#xff0…

计网学习笔记 wireless mobile networks

无线局域网的组成 无线网络在近些年来一直是个非常流行的东西。现在的移动用户数量已经大大超过了有线用户数量&#xff0c;比例大于5:1。 实现无线网络的挑战性 从有线到无线是网络构建上一个伟大的设想&#xff0c;挑战性主要集中在wireless和mobility两个点上。 这两个是…

Express 框架的基本操作

目录 1、应用生成器 2、基本路由 2.1、在跟路由下配置 GET请求&#xff0c;返回对应相应内容。 2.2、在跟路由下配置 POST请求&#xff0c;返回对应相应内容。 2.3、在跟路由下配置 PUT请求&#xff0c;返回对应相应内容。 2.4、在根路由下配置DELETE请求&#xff0c;返回对…

【剑指offer】学习计划day4

目录 一. 前言 二.数组中重复的数字 a.题目 b.题解分析 c.AC代码 三.在排序数组中查找数字 I a.题目 b.题解分析 c.AC代码 四.0&#xff5e;n-1中缺失的数字 a.题目 b.题解分析 c.AC代码 一. 前言 本系列是针对Leetcode中剑指offer学习计划的记录与思路讲解。详情查看以下…