C++ 类和对象 上

news2024/11/27 5:28:47

目录

前言

什么是面向对象?什么是面向过程?

面向过程

面向对象

 比较

引入

定义

实例化

类的大小

this指针


前言

        今天我们来进入C++类和对象的学习。相信大家一定听说过C语言是面向过程的语言,而C++是面向对象的语言?那么他们有什么却别呢?又怎么体现呢?这就与我们今天要说的类和对象脱不了关系了。

什么是面向对象?什么是面向过程?

面向过程

        面向对象是相对于面向过程来讲的,面向对象方法,把相关的数据和方法组织为一个整体来看待,从更高的层次来进行系统建模,更贴近事物的自然运行模式。

        面向过程(Procedure Oriented)是一种以过程为中心的编程思想。这些都是以什么正在发生为主要目标进行编程。

        这是两种方法的官方解释,我们首先要明白的是不论是面向过程还是面向对象他们的最终目的都是解决具体的问题,只是角度不同罢了。下面举个具体的例子来看。

        比如现在我们要洗衣服。如果以面向过程的思想,那么它应该按照如下的思路进行

        紧接着我们用代码标识上述的一些列操作,先定义个盆,设置盛水量等自定义变量,然后写个放水的函数,再放洗衣粉,手搓……最后晾衣服。可以看出面向过程就是按照事物的发展逐步的解决问题,符合我们的直觉,当我们知道具体的过程时,便可以一步步的实现代码,将洗衣服的大事不断化小,分解,最终再汇总就解决了问题。

        当然面向过程的实现代码的难度不会太高,只要我们知道了一件事的过程便可以逐步的解决。化大为小,通过不断地解决子问题最总解决问题。


面向对象

        不难发现,在C语言中数据和函数是分离的。我们不能在自定义的类型如struct中实现函数,要想使用函数,必须给函数传递数据的参数,然后进行处理。但在C++中数据和函数是紧密结合的,像这样的自定义类型不只包含数据,还有函数的就可以称为类。

        还是上面洗衣服的例子。假如我们以面向对象的思想看待这个问题,我们该如何做呢?

        首先我们要抽象出来对象,就可以理解为我们要操作的数据,对象就有人,盆,洗衣机,衣服,而接水,手搓,拧干就可以看为数据的处理,即类的函数。

        

        我们接着就调用人类中的拿衣服函数,然后盆类装衣服函数,假如洗衣粉函数,然后再调用人类中的洗衣函数,最后调用人类中的晒衣服函数。

        可以看出,所谓的面向对象就是从一件事情中抽象出对象,然后在对象间处理事情。

        面向对象看起来十分的简单,只需要不断地调用函数,但这些函数却是要我们自己一一实现的,与面向过程最大的不同是将函数与数据封装在一起。可以说是从另外一个思想角度上解决了问题。但类还有许多巨大的优势,例如简化代码,在类中实现函数不需要传太多的参数,安全性提高,命名冲突大大减少,代码复用性好等,我们在后面会一一介绍。

 比较

        有个恰当的比喻是,面向过程是编年体,以时间为线索记录历史,面向对象是纪传体,以人物为线索记录历史,二者殊途而同归。同样面向过程就是按照我们理解的事情发展而不断地写函数处理,而面向对象是先在类中实现类的各种函数,最后再使用。二者最终都可以解决问题。

        从上面看来说,面向对象和面向过程复杂度没有太大区别,但面向对象又发展出了三大特性,封装,继承,多态。这个我们会在后面说,这几种重要的性质大大简化了面向对象开发的复杂度。

        接下来我们正式的了解类的概念和用法。

引入

        在正式的说类之前,我们先看一个我们熟悉的C语言结构体知识。

struct Date
{
	int year;
	int month;
	int day;
};

void DateInit(struct Date* p,int year,int month,int day)
{
	p->day = day;
	p->month = month;
	p->year = year;
}

void DatePrint(struct Date* p)
{
	printf("%d-%d-%d\n",p->year,p->month,p->day);
}

int main()
{
	struct Date t;
	DateInit(&t, 2024, 4, 11);
	DatePrint(&t);

	return 0;
}

        我们定义了一个日期结构体,然后打印出日期。函数名为了不与其他的函数混淆,同一加上了Date, DatePrint,DateInit,当然就目前的小段程序而言不需要区分,但加了区分是一个好习惯。

        可能会有读者十分疑惑?不是要讲类么,怎么讲了结构体。我们要明白没有人可以什么都从无到有的创造,我们要学会站在巨人的肩膀上。类可以看为C语言结构体的PLUS版。

定义

        类的定义十分简单如下

class/struct className
{
 // 类体:由成员函数和成员变量组成
 
}; 

        其内部的成员又可以被操作符public: private: protected:修饰从而产生不同的用法。是不是与结构体十分相似,看起来不过加个三个修饰符并且支持内置函数。但这几个用法较为复杂,我们后序再做介绍。

        由此我们便可以将上面C语言的代码用C++写出来。

struct Date
{
	int _year;
	int _month;
	int _day;

	void Init( int year, int month, int day)
	{
		_day = day;
		_month = month;
		_year = year;
	}

	void Print()
	{
		printf("%d-%d-%d\n", _year, _month, _day);
	}

};


int main()
{
	Date t;
	t.Init(2024, 4, 11);
	t.Print();


	return 0;
}

        首先我们要明白,类里面的变量,函数存在类的命名空间内,俗称类域。这样我们的函数名就不需要加上Date标识符了,即使其他的类也有Init函数,但他们处于不同的命名空间中,不会发生命名冲突。其次我们也不需要传递结构体的指针了,或者说不需要我们人为的传递指针,在类的里面函数可以直接调用内置类型。是不是相对于C语言来说简化了许多代码了!!

        上述的struct Date也可以称之为类,但大家看见更多的可能是class Date,他们都可以称之为类。但C++为了衔接C语言,将struct Date默认数据类型用public:修饰,class Date默认类型用private:修饰。他们的区别如下。

        当然访问限定符的区别不知有上面的,更多的在类的继承模块,在这里就不多讲了。其次访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止。 如果后面没有访问限定符,作用域就到}即类结束。

        除了默认访问权限的不同,struct Date与class Date基本没有区别,但我们定义类一般用class,与以前的结构体做区分。

        我们保存的日期数据肯定是不希望其他人随便更改的,于是便可以使用访问限定符,只流出一定的接口函数给别人使用,对于最基础的数据禁止直接修改访问。如下代码。

class Date
{
public:
	void Init( int year, int month, int day)
	{
		_day = day;
		_month = month;
		_year = year;
	}

	void Print()
	{
		printf("%d-%d-%d\n", _year, _month, _day);
	}

private:
	int _year;
	int _month;
	int _day;
};

        假如我们在类的外面修改 _year,便会报出如下错误。

实例化

       此时我们再次回到刚才类的代码上。我们思考一个问题?这段代码是声明还是定义?

class Date
{
public:
	void Init( int year, int month, int day)
	{
		_day = day;
		_month = month;
		_year = year;
	}

	void Print()
	{
		printf("%d-%d-%d\n", _year, _month, _day);
	}

private:
	int _year;
	int _month;
	int _day;
};

        我们要区别声明和定义首先要了解他们的区别,简单来说定义就是开辟一段空间然后可以存储对应的数据,而声明不开辟空间,仅仅是告诉编译器有这个变量或者函数。显然我们上述的代码是一段声明,并没有开辟一段空间。而下面一段代码则不同。

Date t;

        它就是定义了一个Date变量,开辟了空间。我们可以把类称之为一种自定义类型的变量类型,所谓实例化就是用这个变量类型开辟空间,定义变量。

        我们可以把类看为房子的设计图纸,而对象就是根据类实现的房子。如下图

类的大小

        说完实例化,我们紧接着来看类的大小,我们前面提到过类与C语言的结构体是血脉相连的,结构体计算内存采用的内存对齐在类中也有。

        下面是结构体内存对齐规则,在类中变量也遵循如下的规则。

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

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

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

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

        但类比C语言多了个函数,那么函数如何计算内存呢?

        我们先看如下使用场景。

int main()
{
	Date t;

	t.Init(2024, 4, 11);
	t.Print();

	Date t2;
	t2.Init(2024, 4, 11);
	t2.Print();
	Date t3;
	t3.Init(2024, 4, 11);
	t3.Print();

	Date t4;
	t4.Init(2024, 4, 11);
	t4.Print();

	return 0;
}

        这个代码十分简单,创建四个对象,并且初始化,打印。对于成员函数而言,他们在接受参数后,会根据参数进行处理。那么我们四次打印,初始化的代码逻辑是不是相同的,只不过实参不同罢了。如果我们给每个对象都保存一份成员函数,是不是大大的浪费了使用空间,于是C++就规定,类的成员函数不计算内存,类的成员函数代码保存在系统的代码区。

        于是我们可以用关键字sizeof求出类的大小,除非不给用sizeof,否则不要自己算!这里为了让大家理解,会带着大家算一遍。

        为什么这样呢?看如下示意图。

        有的读者可能觉得这不就是三个int相加么?但这只是一种巧合,下面我们稍微修改代码在计算一次。

class Date
{
public:
	void Init( int year, int month, int day)
	{
		_day = day;
		_month = month;
		_year = year;
	}

	void Print()
	{
		printf("%d-%d-%d\n", _year, _month, _day);
	}

private:
	int _year;
	char t;
	int _month;
	int _day;
};

        大家可以先自行计算,然后看下面的分析。

        我们也可以用程序检测下。可以看出此时类的大小也为16.

        关于大小我们最后再看一个特殊的情况空类。如下代码

class pr
{

};

        那么他的大小是多少呢?为0还是什么?

        可以看出他的大小为1,这也是C++的规定之一,空类的大小为1.也就是说类的大小至少为1.

        C++做了这个规定,可能也是为了如下代码考虑,用空类定义了一个对象,属于定义还是声明。如果空类的大小为0,那么就不符合定义的基本条件开辟内存,划为声明有有些不和常量,所以最后规定空类的大小为1.

int main()
{
	pr t;
	printf("%d", sizeof(pr));

	return 0;
}

this指针

        接下来我们来认识个C++关键字this,它有什么意义呢?

        我们回头看上面的一段程序

int main()
{
	Date t;

	t.Init(2024, 4, 11);
	t.Print();

	Date t2;
	t2.Init(2024, 4, 11);
	t2.Print();
	Date t3;
	t3.Init(2024, 4, 11);
	t3.Print();

	Date t4;
	t4.Init(2024, 4, 11);
	t4.Print();

	return 0;
}

        对于每个t.Init(2024, 4, 11);操作我们只提供了对应要初始化的值,编译器如何才能找到对于的变量对其进行初始化,而不会找错对象。我们回顾C语言对结构体初始化的程序,如下代码。我们为什么可以精准的找到对应变量并对齐进行初始化。我们传递了个结构体指针保证了我们不会出错。

void DateInit(struct Date* p,int year,int month,int day)
{
	p->day = day;
	p->month = month;
	p->year = year;
}

        回到C++,我们写出t2.Init(2024, 4, 11);的函数,也能初始化,其实是编译器帮我们默认传递了一个类的指针!!在类中用this表示。并不是C++语言多神秘,只是编译器帮我们做了许多底层的工作,帮助我们更好的使用语言。我们也可以简单的看下汇编代码。

        编译器在背后做出了巨大的奉献!!方便我们使用。

        今天的文章就到此结束了,大家喜欢的点点关注!

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

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

相关文章

[当人工智能遇上安全] 13.威胁情报实体识别 (3)利用keras构建CNN-BiLSTM-ATT-CRF实体识别模型

《当人工智能遇上安全》系列将详细介绍人工智能与安全相关的论文、实践,并分享各种案例,涉及恶意代码检测、恶意请求识别、入侵检测、对抗样本等等。只想更好地帮助初学者,更加成体系的分享新知识。该系列文章会更加聚焦,更加学术…

微信小程序全屏开屏广告

效果图 代码 <template><view><!-- 自定义头部 --><u-navbar title" " :bgColor"bgColor"><view class"u-nav-slot" slot"left"><view class"leftCon"><view class"countDown…

2024-简单点-观察者模式

先看代码&#xff1a; # 导入未来模块以支持类型注解 from __future__ import annotations# 导入抽象基类模块和随机数生成器 from abc import ABC, abstractmethod from random import randrange# 导入列表类型注解 from typing import List# 定义观察者模式中的主体接口&…

Rocky(Centos)数据库等高并发或高io应用linux系统调优,及硬件问题排查(含网络、磁盘、系统监控)

一、系统参数优化 默认的最大打开文件数是1024.不满足生产环境的要求。按照如下配置&#xff1a; 1、修改 systemctl管理的 servie 资源限制 编辑/etc/systemd/system.conf # 全局的打开文件数 DefaultLimitNOFILE2097152 # 全局打开进程数 DefaultLimitNPROC655352、调整系…

【Vue3 + ElementUI】表单校验无效(写法:this.$refs[‘formName‘].validate((valid) =>{} ))

一. 表单校验 1.1 template模块 el-form 中 若校验&#xff0c;ref 和 rules 必须要有 <template><div style"padding:20px"><el-form ref"formName" :model"form" :rules"formRules" label-width"120px"…

Docker 学习笔记(七):介绍 Dockerfile 相关知识,使用 Dockerfile 构建自己的 centos 镜像

一、前言 记录时间 [2024-4-12] 系列文章简摘&#xff1a; Docker学习笔记&#xff08;二&#xff09;&#xff1a;在Linux中部署Docker&#xff08;Centos7下安装docker、环境配置&#xff0c;以及镜像简单使用&#xff09; Docker 学习笔记&#xff08;三&#xff09;&#x…

LDF、DBC、BIN、HEX、S19、BLF、ARXML、slx等

文章目录 如题 如题 LDF是LIN报文格式文件&#xff0c;把这个直接拖到软件里面&#xff0c;可以发报文和接收报文 DBC是CAN报文格式文件&#xff0c;把这个直接拖到软件里面&#xff0c;可以发报文和接收报文 BIN文件烧录在BOOT里面&#xff08;stm32&#xff09;&#xff0c…

适用于 Windows 的 10 个免费数据恢复工具集合

有时&#xff0c;我们都会在个人计算机上意外删除一些重要文件或数据。我们无需再担心此类问题&#xff0c;因为我们可以借助互联网上提供的免费数据恢复工具来恢复宝贵的数据和图像。 互联网上有许多免费的数据恢复工具&#xff0c;从一长串工具中&#xff0c;我们列出了最好…

蓝桥杯-STL-string

目录 字符串定义 字符串初始化 字符串输入输出 字符串输出 字符串输入 字符串访问 字符串拷贝 字符串拼接 直接相加 append(const char*str,int n) 字符串比较 ​编辑字符串长度length()/size() 字符串查找find(string str) 查找子串substr(int a,int b) 字符串的…

webpack-loader的使用

引入css后执行打包命令 "build": "npx webpack --config wk.config.js"发现报错&#xff1a; webpack默认只能处理js其他的像css,图片都需要借助loader来处理 css-loader loader可以用于对模块的源代码进行转换&#xff0c;可以把css看成一个模块&…

Transformer模型-decoder解码器,target mask目标掩码的简明介绍

今天介绍transformer模型的decoder解码器&#xff0c;target mask目标掩码 背景 解码器层是对前面文章中提到的子层的包装器。它接受位置嵌入的目标序列&#xff0c;并将它们通过带掩码的多头注意力机制传递。使用掩码是为了防止解码器查看序列中的下一个标记。它迫使模型仅使用…

【高效开发工具系列】obsutil安装与使用

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

广州图创 图书馆集群管理系统 updOpuserPw SQL注入漏洞复现

0x01 产品简介 广州图创计算机软件开发有限公司是集产品研发、应用集成、客户服务为一体的高新技术企业,主要目标是为图书馆行业用户提供高质量的应用软件系统设计、集成和维护服务。 0x02 漏洞概述 由于广州图创 图书馆集群管理系统 updOpuserPw 接口处未对用户输入的SQL语…

minio-docker单节点部署SDK测试文件上传下载

目录 一&#xff0c;docker部署minio单节点单磁盘 二&#xff0c;SDK测试上传下载 一&#xff0c;docker部署minio单节点单磁盘 1.拉取镜像 # 下载镜像 docker pull minio/minio 2.查看镜像 docker images 3.启动minio(新版本) 创建本机上的挂载目录&#xff0c;这个可以…

鸿蒙OS开发学习:【第三方库调用】

介绍 本篇Codelab主要向开发者展示了在Stage模型中&#xff0c;如何调用已经上架到[三方库中心]的社区库和项目内创建的本地库。效果图如下&#xff1a; 相关概念 [Navigation]&#xff1a;一般作为Page页面的根容器&#xff0c;通过属性设置来展示页面的标题、工具栏、菜单。…

【AIGC】本地部署通义千问 1.5 (PyTorch)

今天想分享一下 Qwen 1.5 官方用例的二次封装&#xff08; huggingface 说明页也有提供源码&#xff09;&#xff0c;其实没有太多的技术含量。主要是想记录一下如何从零开始在不使用第三方工具的前提下&#xff0c;以纯代码的方式本地部署一套大模型&#xff0c;相信这对于技术…

50. QT/QML中创建多线程的方式汇总

1. 说明 在QT / QML中创建线程主要有三种方式。第一种:在定义类时继承 QThread 这个类,然后重写父类的虚函数 run(),将子线程需要执行的业务代码放到 run() 函数当中即可。**注意:**这种方式官方已经摒弃了。第二种:使用moveToThread()函数将需要在子线程中执行的函数类移…

软件设计师-基础知识科目-标准化与软件知识产权基本知识11

十一、标准化与软件知识产权基本知识&#xff1a; 知识产权&#xff1a; 主要包括&#xff1a;著作权及邻接权、专利权、工业品外观设计权、商标权、地理标志权、继承电路布图设计权。邻接权是指与著作权相邻近的权利&#xff0c;是指作品传播者&#xff0c;对其传播作品过程…

文献速递:深度学习肝脏肿瘤诊断---基于多相增强 CT 和临床数据的恶性肝肿瘤鉴别诊断深度学习

Title 题目 Deep learning for diferential diagnosisof malignant hepatic tumors based on multi-phase contrast-enhanced CT and clinical data 基于多相增强 CT 和临床数据的恶性肝肿瘤鉴别诊断深度学习 Abstract 摘要 Liver cancer remains the leading cause of can…

中介者模式:简化对象间通信的协调者

在面向对象的软件开发中&#xff0c;中介者模式是一种重要的行为型设计模式&#xff0c;用于降低多个对象间通信的复杂性。通过提供一个中心化的对象来处理不同组件之间的交互&#xff0c;中介者模式使得组件间不必显式引用彼此&#xff0c;从而使其松散耦合、更易于维护。本文…