【C++】类和对象(上篇)

news2025/1/21 12:15:35

类和对象

    • 面向过程和面向对象初步认识
    • 类的引入
    • 类的定义
    • 命名规范
    • 类的访问限定符及封装
      • 访问限定符
      • 封装
    • 类的作用域
    • 类的实例化
    • 类的对象大小的计算
    • 类成员函数的this指针
      • this指针的引出
      • this指针的特性

在这里插入图片描述

面向过程和面向对象初步认识

C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。

举个栗子:
比如说我们现在要洗衣服,那么就要大概进行下面的步骤:
在这里插入图片描述
我们可以对每个步骤都写一个函数,是不是很麻烦?

但如如果换成洗衣机呢?
在这里插入图片描述

没有理解面向对象和面向过程的话没关系,对于刚入门的同学来说,想要有一定的理解有点太过于强求了。

直接上知识:

类的引入

C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。
比如:在数据结构中,用C语言方式实现的栈,结构体中只能定义变量;现在以C++方式实现,会发现struct中也可以定义实现栈的函数。

下面把例子给出(实现方面的就不写了):

typedef int DataType;

struct Stack
{
	void StackInit()
	{
		//栈的初始化操作
	}

	void StackPush(DataType x)
	{
		//入栈操作
	}

	void StackPop()
	{
		//出栈操作
	}

	DataType* _data;
	int _sz;
	int _top;
};

可以看到,我们现在可以在struct内部既可以写变量,又可以写函数。

而且我们在用结构体定义一个变量时不需要再写struct了,直接写跟在struct后面的Stack就行,编译器是不会报错的。
在这里插入图片描述
可以说C++中的struct就是C语言中的strcut的升级版本。不仅包含了C的用法,还新增了用法。

但是这个升级的结构体,C++一般不用,更多用的是类class。

类的定义

class className
{
// 类体:由成员函数和成员变量组成
};  // 一定要注意后面的分号

class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。

C++中用{}括起来的都叫域,域要么影响生命周期,要么影响访问。

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

类的两种定义方式:

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

  2. 类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::

例子:
在这里插入图片描述
在这里插入图片描述

那么类的定义的的时候推荐:

  1. 小函数(语句比较少),直接在类内定义,当作内联函数,提高程序运行效率
  2. 大函数(语句比较多),声明和定义分离,每次可以直接看到每个函数的声明,增加可读性

下面看几个例子:

class Date
{
public:
	void Init(int year)
	{
		// 这里的year到底是成员变量,还是函数形参?
		year = year;
	}
private:
	int year;
};

在这里插入图片描述
根据编译器的提示(背景颜色)可以知道形参year和下面的两个year是一样的。

所以说这种命名风格是非常挫的。

这样写的话就好多了:

class Date
{
public:
	void Init(int year)
	{
		_year = year;
	}
private:
	int _year;	
};

所以就要讲讲命名规范了:

命名规范

这里主要讲一下驼峰法。

每个单词之间的单词开头要用大写,比如说:MyName,不要写成myname,后者的可读性很差。

对于函数、类,首单词的首字母大写,后面单词首字母大写。
对于变量,首单词的首字母小写,后面单词首字母大写。
对于成员变量,首单词前面加上_。

类的访问限定符及封装

访问限定符

各位在上面已经见过public和private这两个关键字了,下面说这两和protected是干啥用的。

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

在这里插入图片描述
同过这三个限定符,可以控制类内的东西是否能在类外被用。

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

新手可能看不懂,马上给例子,但是注意第五点是class和struct的一个区别。

public修饰的成员在类外可以直接被访问, protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的):
在这里插入图片描述
目前阶段记住protected和private的是一样的(错误说法),只有到了之后继承那块才会有区别

访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止;如果后面没有访问限定符,作用域就到 } 即类结束。:
在这里插入图片描述
上面的public到private位置后权限由公有变为私有。
private到了}后就没了。

C++中struct和class的区别是什么?
C++需要兼容C语言,所以C++中struct可以当成结构体使用。另外C++中struct还可以用来定义类。和class定义类是一样的,区别是struct定义的类默认访问权限是public,class定义的类默认访问权限是private。
注意:在继承和模板参数列表位置,struct和class也有区别,后序给大家介绍。

封装

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

在类和对象阶段,主要是研究类的封装特性,那什么是封装呢?

封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
在C++语言中实现封装,可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用。

上面的说法太过于官方了,给个例子就懂了。

比如说我现在用C语言写了一个栈,还用C++写了一个栈。

C语言实现的时候,标准情况下,我们会去实现一个得到栈顶元素的一个函数,
这时候想要得到栈顶元素只需要StackTop()就可以了,但是有的人在实现的时候不会,他们在获取栈顶元素的时候会直接访问数组的元素,就是data[top],这时候会引发歧义,因为栈顶初值有两种实现方式,一种是初值为0,一种是初值为-1,这样的话若是没有定义得栈顶元素得函数,就会导致不同用户使用不同的方法,就乱了。

C++得话,我们直接将成员变量设置成private的,就不会出现上面的情况,只能实现一个public的函数来获取栈顶元素。这就叫封装。用户在用的时候就只能通过函数来搞了。

所以:
C语言,没办法封装,可以使用函数访问数据,也可直接访问数据,使用者可能出错,不够规范。

C++:封装必须规范使用函数访问数据,不能直接访问数据。可以使用户更方便的使用。

类的作用域

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

在这里插入图片描述
这个在前面类的成员函数的声明和定义分离的时候讲过了。

类的实例化

用类类型创建对象的过程,称为类的实例化

  1. 类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它;比如:入学时填写的学生信息表,表格就可以看成是一个类,来描述具体学生信息。
    在这里插入图片描述
  2. 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量Person类是没有空间的,只有Person类实例化出的对象才有具体的年龄。
  3. 做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间。

类的对象大小的计算

其实就和结构体差不多,都有内存对齐。如果不懂内存对齐的话建议看下我这篇博客:只用看结构体的内存对齐就行

我们看一下上面的例子Person的大小:
在这里插入图片描述

可能有的同学就有疑惑了,但是如果你光算里面的成员变量的话,内存对齐后就是28,函数没有统计大小。

那么有三种存储的模型,来看看:

在这里插入图片描述
缺陷:每个对象中成员变量是不同的,但是调用同一份函数,如果按照此种方式存储,当一个类创建多个对象时,每个对象中都会保存一份代码,相同代码保存多次,浪费空间。那么如何解决呢?

代码只保存一份,在对象中保存存放代码的地址
在这里插入图片描述

只保存成员变量,成员函数存放在公共的代码段
在这里插入图片描述
在这里插入图片描述

对于上述三种存储方式,那计算机到底是按照那种方式来存储的?
我们再通过对下面的不同对象分别获取大小来分析看下:

在这里插入图片描述
在这里插入图片描述
空类:在这里插入图片描述

第一种是不太可能了。

看第二个例子:如果是第二种存储方式的话,那么此处的大小就应该是4,因为有一个函数表的指针。但是这里的大小是1。所以第二种方式也排除了。

那就是第三种喽。也就是说,每个实例化的对象中的变量是相互独立的,但是类对象在使用成员函数的时候是在使用同一个函数。在编译链接时,就根据函数的名去公共的代码区找到函数的地址,call函数地址。

结论:一个类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐
注意空类的大小,空类比较特殊,编译器给了空类一个字节来占位,用来唯一标识这个类的对象存在,不存储实际数据。

类成员函数的this指针

this指针的引出

我们先来定义一个日期类 Date

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类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?

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

this指针的特性

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

在这里插入图片描述

给几个例子:

成员函数
在这里插入图片描述在这里插入图片描述在这里插入图片描述
但是上面编译器处理的我们是看不到的,而且也不能自己去显示的定义this指针这个参数但是可以使用this指针去取出this指针指向的对象的成员变量。

函数调用
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
当然,我们也不能显示传递这个对象的地址,这些都是由编译器来完成的。

看道题:
在这里插入图片描述
在这里插入图片描述
这里竟然能够成功运行。

原因是,实例的对象中存放的是成员变量,并没有存放成员函数,成员函数是在公共代码区中的,调用这个函数并不是去这个对象中找,所以并不是去解引用d1,编译链接时就根据函数名去公共代码区找到函数的地址。
在这里插入图片描述
但是如果调用的函数中解引用了程序就崩掉了。
在这里插入图片描述
这是因为,这里的this指针实际上是空指针,也就是d1。
在这里插入图片描述
如果在函数内部解引用了this就是解引用了空指针,这样程序就崩掉了。

而上面的那个函数中并没有解引用this指针,所以就不会出现崩掉的情况。

this指针存放在哪里?
this指针是形参,存放在栈区。

但有的编译器还会对this指针做优化处理,直接将this指针放到寄存器中。在这里插入图片描述

到此为止。。。。

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

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

相关文章

RocketMQ-Producer

消息生产者的代码都在client模块中&#xff0c;相对于RocketMQ来讲&#xff0c;消息生产者就是客户端&#xff0c;也是消息的提供者。 启动流程 代码&#xff1a;DefaultMQProducerImpl#start public void start(final boolean startFactory) throws MQClientException {switc…

轻松掌握消息队列RabbitMQ在SpringAMQP中的实践知识点

1、介绍 spring集成了rabbitmq&#xff0c;可以对rabbitmq先进行安装简单了解。参考我的rabbitmq文章。 2、使用 1、基本消息队列BasicQueue案例 一个消息消费者&#xff0c;上个消息未处理完&#xff0c;队列中的消息将阻塞&#xff0c;导致内存泄漏 1、引入AMQP依赖 2、添…

读书:《科技论文写作与发表教程(第6版)》

科技写作是指以符合标准格式的科技论文形式在科技期刊上陈述原创性的研究。 另外&#xff0c;还有广义上的科技写作。 科技写作的最主要特点是表达清晰。科技写作不需要漂亮的文学修饰&#xff0c;要把信息清楚地传递给读者。 IMRAD格式&#xff1a;Introduction Methods Re…

Redis常见问题整理

一、Redis使用场景相关问题 0. 单机版Redis部署 系统环境&#xff1a;CentOS7 1、下载Redis所需要的镜像 yum install -y gcc tcl2、下载redis安装包 mkdir /soft cd /soft wget https://download.redis.io/releases/redis-6.2.4.tar.gz3、解压缩&#xff1a; tar -xvf redi…

QT-DAY3

实现ui 字体、颜色、保存文件、打开文件 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//字体按钮对应的槽函数 void Wid…

性能测试技术笔记:如何设计一个压测平台 ?

目录 为什么需要压测平台&#xff1f; 压测平台功能设计思路 压测平台技术实现方案 总结 为什么需要压测平台&#xff1f; 从实际工作场景出发&#xff0c;如果只有一两个人做性能测试工作&#xff0c;那其实没必要开发专门的压测平台&#xff0c;原因如下&#xff1a; 成…

【备战秋招】权限常见面试题

本文的面试题和答案均为本人自己收集&#xff0c;如有错误或者不足&#xff0c;欢迎大家指出 目录 你做过的系统&#xff0c;权限是怎么管理的 Shiro是靠什么做认证和授权的 什么是RBAC模型 如果让你手写一个Web过滤器验证权限&#xff0c;你会怎么写 Shiro的anon和authc都…

锅炉燃烧自动控制系统

串级控制 以外环温度、内环煤气流量为例 重点1 主PID输出0~100需要经过线性转换模块进行转换&#xff0c;转换成与煤气流量相同量纲才能作为副PID的设定值。假设煤气流量量程100000&#xff1b;则副PID设定值如下&#xff1a; secSET mainLMN/100*100000&#xff1b; 重点2…

创造者基金 2023 年 4 月亮点

隆重推出创作者基金的 2023 年 4 月亮点 NFT 系列——一系列令人着迷且令人惊叹的数字资产&#xff0c;让大家为之震撼&#xff01;该系列的角色令人惊叹&#xff0c;包括阿兹特克酋长、维京战士、残酷的国王、传奇的九尾狐等等&#xff0c;是收藏家和爱好者的必备之物。 无论你…

优化 | 随机原始对偶混合梯度(SPDHG)算法及在图像处理中的应用

论文解读者&#xff1a;陈宇文&#xff0c;胡明杰&#xff0c;史铭伟&#xff0c;赵田田 许多实际问题都可以建模为凸优化问题。相比于直接求解原问题&#xff0c;将问题转化为鞍点问题往往会带来好处。求解鞍点问题的一种常用算法是原对偶混合梯度算法 (PDHG)&#xff0c;它在…

ubuntu-server22.04编译Redis7.0.11源码支持TLS

1.克隆redis源码: git clone https://github.com/redis/redis.git 编译前确认已安装GCC11与G++11和cmake及make及pkg-config 安装命令如下: apt install gcc -y apt install g++ -y apt install cmake -y apt install pkg-config 因为要支持TLS所以要安装OPENSSL开发库 ap…

阿里云CPFS与OSS之间数据双向流动机制

随着云上对象存储成本的逐渐降低&#xff0c;越来越多的企业利用阿里云OSS存储他们的大量数据并构建数据湖。现在阿里云文件存储CPFS与对象存储OSS实现了深度集成&#xff0c;客户可以在三十分钟内创建一个高性能CPFS文件系统并链接到他们的OSS bucket。当链接到OSS bucket以后…

实验二 ROS结合OpenCV示例——人脸识别

ROS结合OpenCV示例——人脸识别 一、实验原理&#xff1a;二、实验步骤&#xff1a;<1> 安装opencv 以及串口功能包<2> 测试opencv串口是否安装成功 三、程序分析&#xff1a; 一、实验原理&#xff1a; Opencv库是一个基于BSD许可发行的跨平台开源计算机视觉库&a…

STM32WB55_NUCLEO开发(9)----接收手机数据点亮LED

概述 本篇文章主要介绍如何使用STM32CubeMX对生成STM32WB工程&#xff0c;并通过与STM32WB配对&#xff0c;向该特征写入一个任意字节&#xff0c;绿色LED会切换。 硬件准备 首先需要准备一个开发板&#xff0c;这里我准备的是NUCLEO-WB55RG 的开发板&#xff1a; 选择芯片…

加拿大各省接受公立教育的初始年龄汇总 — 供携子女赴加的访学、博后参考

近年来到加拿大从事访问学者和博士后研究的申请者日益增多&#xff0c;有些申请者想带孩子同去上公立学校。因为加拿大各省教育局政策有差异&#xff0c;所以入学&#xff08;包括学前班&#xff09;年龄不同&#xff0c;为此知识人网小编整理本文为大家解惑答疑。 加拿大为本国…

GitHub Actions自动发布Package到Pub.dev

一、创建package或plugin 先创建一个package或者plugin 二、手动上传第一个版本到pub.dev flutter packages pub publish --serverhttps://pub.dartlang.org 三、在admin配置自动化发布 打开pub.dev中的对应的package按照以下图片配置 四、在项目跟目录配置发布脚本 1、在…

XMLMapperBuilder解析*mapper.xml

springboot的MybatisAutoConfiguration自动配置类会创建SqlSessionFactory&#xff0c;创建过程就是填充configuration属性&#xff0c;调用buildSqlSessionFactory()方法完成SqlSessionFactory创建&#xff0c;这其中就会创建XMLMapperBuilder解析mapper.xml和XMLConfigBuilde…

UHD在DPDK下进行编译

1.安装choco windows环境,用管理员权限打开 powershell 命令行界面。 输入命令:Set-ExecutionPolicy AllSigned 继续输入命令:Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]…

C++---区间DP/高精度计算---凸多边形的划分(每日一道算法2023.4.27)

注意事项&#xff1a; 本题是"区间DP—能量项链"的扩展题&#xff0c;可以先理解下那道题。 本题使用了"高精度乘法"和"高精度加法"&#xff0c;可以去这两篇文章看&#xff0c;有详解。 题目&#xff1a; 给定一个具有 N 个顶点的凸多边形&…

什么是SSO?

SSO&#xff08;Single Sign On&#xff09;单点登录。SSO是在多个应用系统中&#xff0c;用户只需要登录一次就可以访问所有相互信任的应用系统。它包括可以将这次主要的登录映射到其他应用中用于同一个用户的登录的机制。它是目前比较流行的企业业务整合的解决方案之一。 当…