【C++】类和对象 - 上

news2024/9/23 17:24:27

目录

  • 1. 面向过程和面向对象初步认识
  • 2. 类的引入
  • 3. 类的定义
  • 4. 类的访问限定符及封装
    • 4.1 访问限定符
    • 4.2 封装
  • 5. 类的作用域
  • 6. 类的实例化
  • 7. 类对象模型
    • 7.1 如何计算类的大小
    • 7.2 类对象的存储方式猜测
    • 7.3 结构体内存对齐规则
  • 8. this指针
    • 8.1 引出
    • 8.2 this指针的特性
  • 总结

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

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

比如说洗衣服,一般分为以下几个步骤:
在这里插入图片描述
以上的每一个步骤都是需要人来做的,也就是说人是要对每个步骤应该做的事情都要清楚,在语言层面对应依次设计和实现函数来解决遇到的每个问题,这就是面向过程的大体思想。

C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。

同样是洗衣服,用面向对象的思路则是要把解决的问题分为几个对象:人、衣服、洗衣机,洗衣粉。
洗衣服的过程则为:人把衣服放进洗衣机–>倒入洗衣粉–>启动洗衣机–>最后洗衣机洗完会把衣服甩干。
这里的整个过程主要是这几个对象之间互相交互完成的,而有些步骤,人是不需要知道的,比如洗衣机是如何洗衣服以及如何甩干的,这是面向对象的思想。

2. 类的引入

C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。因为C++把结构体升级成了类:

//C语言只能这么玩
struct stu 
{
	//成员变量
	char name[20];
	int age;
	//...
};

//C++
struct stu 
{
	//成员函数
	char* getName() {
		return name;
	}
	int getAge() {
		return age;
	}
	//...
	
	//成员变量
	char name[20];
	int age;
	//...
};

同样定义一个结构体对象也多了一种方式:

int main()
{
	//C语言只能像下面这样定义结构体变量
	struct stu s1;
	//C++不仅可以支持C语言的写法
	//下面这种写法也是支持的
	stu s2;
	//stu是类名,直接定义变量
	return 0;
}

C++在语法层面上是完全兼容C的,虽然上面的类型是结构体但是编译器已经把它识别成了类,所以C++更喜欢用关键字class来代替struct

在C++中,用类或者结构定义的变量一般都称之为对象。

3. 类的定义

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

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

与定义结构体的语法是一样的。

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

类的两种定义方式:

  1. 声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。
  2. 类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:如果定义与声明分离,那么在定义处成员函数名前需要加 类名::,否则编译器无法识别是成员函数还是普通全局函数,如下:
class stu 
{
	char* getName();
	int getAge();
	char name[20];
	int age;
	//...
};
char* stu::getName() {
	return name;
}
int stu::getAge() {
	return age;
}

若类中定义的成员函数过多,尽量使用第二种方法,这样有利于提高代码的可读和可维护性。

成员变量命名规则建议:

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

为了避免上述不必要的争议,建议把成员变量的名称都加上一些前后缀来区分,比如:

class Date
{
public:
	void Init(int year)
	{
		_year = year;
	}
private:
	//前缀加上一个下划线
	int _year;
	//或则
	int Year;
};

其他方式也可以的,主要目的是为了区分。

4. 类的访问限定符及封装

4.1 访问限定符

struct改为class后,下面代码编译后会报错:
.cn/236d96a7c0034203996011d74454045c.png)

在这里插入图片描述
原因是C++在类中新增了三个访问限定符,分别是:
在这里插入图片描述
说明如下:

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

从最后一条可以得知报错的原因是:class定义的类,类中的成员变量和成员函数的默认访问权限是private,即外部无法直接访问类中的任何成员,而struct可以是因为它的默认访问权限是public

注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别

为了使得该段代码能够正常通过编译,一种做法是在前面加上public访问限定符:

class stu {
public:
	char* getName();
	int getAge();
	char name[20];
	int age;
};

这样便可顺利通过编译,但是出于安全性考虑,大部分情况是类中的成员变量不想让外部直接被访问到,但是又需要使用这些变量,因此为了满足这种情况,只需要把成员函数公有即可,成员变量进行私有保护:

class stu {
public:
	char* getName();
	int getAge();
private:
	char name[20];
	int age;
};

这种做法很好的保护了成员变量被非法访问,若要访问,必须通过对外提供的接口来安全访问这些私有成员。

你要访问使用我,可以,但是你必须要按照我给你提供的方法来操作

问题:C++中struct和class的区别是什么?

答:C++需要兼容C语言,所以C++中struct可以当成结构体使用。另外C++中struct还可以用来定义类。和class定义类是一样的,区别是struct定义的类默认访问权限是public,class定义的类默认访问权限是private。

注意:在继承和模板参数列表位置,struct和class也有区别,后序会介绍

4.2 封装

封装是面向对象的三大特性之一,另外两个分别为:继承和多态。

另外两个后续会介绍

封装不仅在语言中很常见在现实生活中也是如此,那么什么是封装?
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性或者一些方法的实现细节,仅对外公开接口来和对象进行交互。

举个现实中例子:
计算机的组成以及底层的工作原理十分复杂,但是对于使用者而言压根不太需要关心,只需要知道怎么开机、怎么使用鼠标键盘等来操作计算机就够了。
因此厂商在出厂时,在外部套上壳子,把内部的组成和实现细节全部隐藏起来,对外仅仅暴露出电源以及部分io接口,能让用户和计算机交互即可。

不仅方便了用户,也对计算机本体进行了很好的保护

这是现实层面,在语言层面C++的封装是怎么体现的呢?
它是通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用。

因此封装的本质是一种管理,能让用户更方便的使用类。

5. 类的作用域

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

在成员函数内部使用一个变量或者函数时,会先在该函数的局部域中查找,若找不到再去类域中去找,还找不到则会去全局域中去找,找不到就报错,有命名空间也不会去命名空间中去找。

同样可以指定去命名空间域中去找,找不到就报错

6. 类的实例化

用类类型创建对象的过程,称为类的实例化或者叫做对象的定义:

class stu {
public:
	char* getName();
	int getAge();
private:
	char name[20];				  
	int age;
};
int main()
{
	//对象的实例化
	stu s1;
	stu s2;
	return 0;
}

类类型和结构体类型是一样的,本质只是定义一个新的类型,类型是不占用内存空间的,只有用该类型实例化出一个对象(变量)才会在内存中开辟空间来存储类中声明的那些成员。

就好比建筑图纸,图纸只有一份,并不占地方,只有通过图纸建造出很多实体建筑才会占用地方

在这里插入图片描述

7. 类对象模型

7.1 如何计算类的大小

先说结论:计算一个类的大小与计算一个结构体类型的大小规则是完全一致的。

class stu {
public:
	char* getName();
	int getAge();
private:
	char name[20];				  
	int age;
};
int main()
{
	cout << sizeof(stu) << endl;
	return 0;
}

这里的运行结果为24,字符数组占20个字节,整形占了4个字节,非常奇怪的是类中的成员函数并没有占用空间,这是为什么?

先举个简单的例子:一个小区有多户人家,每家的房子一定都是独立的,都有各自的卧室、客厅和厨房等等,但是若要建一个游泳池或者健身房有没有必要给每一家都建一个呢?是可以的,但是没必要,因为这类建筑对于每户人家的作用是一致的,会造成大量的空间浪费,所以比较好的方法是把它们独立出来在一个公共的区域去建,这样所有人都可以用,很大程度的减少了空间的浪费。

有了这个例子对于上面的问题就比较好理解了,其实类比到类,是一样的。
多户人家可以当作多个实例化出来的对象,每个对象中的成员变量可以当作对应的卧室、客厅和厨房,成员函数则当作健身房,其中成员变量必须是存储在不同的空间中,而因为每个对象中的成员函数的作用是相同的,要是每个对象都存储的话会造成空间浪费,因此在存储的时候可以把它们独立出来放在一块公共的代码段,不同的对象都可以找到然后调用它。

7.2 类对象的存储方式猜测

关于类对象的存储模型有以下三种:

  1. 对象中包含类的各个成员
    在这里插入图片描述
    这种做法是有缺陷的,每个对象中成员变量是不同的,但是调用同一份函数,如果按照此种方式存储,当一个类创建多个对象时,每个对象中都会保存一份代码,相同代码保存多次,浪费空间。

  2. 代码只保存一份,在对象中保存存放函数的地址
    在这里插入图片描述
    很大程度的解决了第一种空间浪费的问题,但没有完全解决。

  3. 只保存成员变量,成员函数存放在公共的代码段
    在这里插入图片描述
    这种是最优的一种实现方法,有了上面提到的例子,可以发现的是类对象的存储模型就是采用的第三种方法,完美解决了空间的浪费。

注意:没有成员变量的类或者空类的大小只有一个字节,作为占位符不存储有效数据,告诉编译器对象存在。

7.3 结构体内存对齐规则

最开始提到过,计算类的大小与计算结构体类型大小的规则是一样的,都要遵循结构体内存对齐原则,具体的在这篇文章中详细地介绍了,这里不再赘述。

8. this指针

8.1 引出

先定义一个日期类:

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;
}

输出结果:
在这里插入图片描述
在主函数中定义了两个对象,分别调用Print函数输出了不同的日期,而两个对象都是调的同一个函数,上面说过不同的对象它们的成员函数所在的区域则是相同的,函数体中也没有不同对象的区分,那么函数是如何识别是哪个对象调用了我呢?

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

因此上面的代码在被编译器处理后变成下面这样子:

 //所有的非静态成员函数都会被处理,不一一写了

//处理前:
void Print(){
	cout << _year << "-" << _month << "-" << _day << endl;
}
d1.Print();
d2.Print();

//处理后:
void Print(Date* const this) {
	cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
}
 d1.Print(&d1);
 d2.Print(&d2);

需要注意的是,this指针传参是编译器的工作,用户不需要显式地传,也不能,否则会报错,但是却可以在函数体里显式地使用this指针,因为有些场景会使用它。

8.2 this指针的特性

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

两个题:

  1. this指针存在哪里?
    this指针虽然是编译器隐式传递的参数,但本质也是一个形式参数,是参数就会在函数调用时被依次压入到栈中,函数调用结束形参也随之被销毁了。
  2. this指针可以为空吗?
    可以,但如果为空,则不可以在函数体中解引用访问对应的成员变量,因为对空指针解引用会报错。

总结

C++中的类是从C语言里的结构体进化而来,并且引入了面向对象的思想,即封装,使得成员变量和成员方法可以一起放在类中,使其之间的联系更加紧密,同时增加了类的访问限定符,不仅使得访问类中的成员更加规范化,而且也屏蔽了底层的部分实现细节,提高了代码的保密性。

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

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

相关文章

ES查看集群信息(健康状态、分片、索引等)

1、查看集群状态使用频率最高的方法 http://192.168.1.101:9200/ 注意&#xff1a;不同环境的ip不同 一般我们通过这个方式来验证ES服务器是否启动成功。 2、_cat/health 查看集群健康状态 [rootCENTOS01 ~]# curl http://192.168.1.101:9200/_cat/health?v epoch tim…

Spring Data【Spring Data Redis、Spring Data ElasticSearch】(二)-全面详解(学习总结---从入门到深化)

目录 四、 Spring Data Redis 五、 Spring Data ElasticSearch 四、 Spring Data Redis Redis 是一个基于内存的数据结构存储系统&#xff0c;它可以用作数据库或者缓存。它支持多种 类型的数据结构&#xff0c;这些数据结构类型分别为 String&#xff08;字符串&#xff09…

0基础系列C++教程 从0开始 第四课

目录 来学习新的内容吧 1 输入 第四课课后习题1&#xff1a; 2 变量 怎么定义变量&#xff1f; 定义变量 第四课课后习题2&#xff1a; 来学习新的内容吧 1 输入 C中既然有了输出 那必然有输入 这时 我们就要用到 cin 函数啦 cin 用法与cout 相似 但却有一个差异 co…

Vue(十八):echarts地图省市区联动

效果 数据来源 https://datav.aliyun.com/portal/school/atlas/area_selector 接口请求地址 https://geo.datav.aliyun.com/areas_v3/bound/geojson?code100000_full 源码 样式 .map {width: 1000px;height: 700px; }布局 <template><div class"map">…

linux -网络编程-多线程并发服务器

目录 1.三次握手和四次挥手 2 滑动窗口 3 函数封装思想 4 高并发服务器 学习目标&#xff1a; 掌握三次握手建立连接过程掌握四次握手关闭连接的过程掌握滑动窗口的概念掌握错误处理函数封装实现多进程并发服务器实现多线程并发服务器 1.三次握手和四次挥手 思考: 为什么…

linux centos7 安装java17

删除旧版本的java或者说是自带的&#xff0c;免得干扰 查找java rpm -qa|grep java java-1.8.0-openjdk-1.8.0.262.b10-1.el7.x86_64 javapackages-tools-3.4.1-11.el7.noarch tzdata-java-2020a-1.el7.noarch python-javapackages-3.4.1-11.el7.noarch java-1.8.0-open…

力扣热门100题之矩阵置0【中等】

题目描述 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]] 示例 2&#xff…

❤️创意网页:萌翻少女心的果冻泡泡 - 创造生动有趣的视觉效果

✨博主&#xff1a;命运之光 &#x1f338;专栏&#xff1a;Python星辰秘典 &#x1f433;专栏&#xff1a;web开发&#xff08;简单好用又好看&#xff09; ❤️专栏&#xff1a;Java经典程序设计 ☀️博主的其他文章&#xff1a;点击进入博主的主页 前言&#xff1a;欢迎踏入…

计算机网络(Computer Networks)基础

本篇介绍计算机网络的基础知识。 文章目录 1. 计算机网络历史2. 以太网" (Ethernet)2.1 以太网" (Ethernet)的简单形式及概念2.2 指数退避解决冲突问题2.3 利用交换机减少同一载体中设备2.4 互联网&#xff08;The Internet&#xff09;2.5 路由(routing)2.6 数据包…

渗透测试基础知识(1)

渗透基础知识一 一、Web架构1、了解Web2、Web技术架构3、Web客户端技术4、Web服务端组成5、动态网站工作过程6、后端存储 二、HTTP协议1、HTTP协议解析2、HTTP协议3、http1.1与http2.0的区别4、HTTP协议 三、HTTP请求1、发起HTTP请求2、HTTP响应与请求-HTTP请求3、HTTP响应与请…

typedef对类型的重命名

typedef 重命名的类型 重命名后的类型名 typedef重命名函数指针类型时的特别写法 正确的重命名函数指针类型的方式 运用&#xff1a; 用typedef简化下面这个代码 简化后&#xff1a;

【C语言day11】

数据类型的等级从低到高如下&#xff1a;char int long float double运算的时候是从低转到高的&#xff0c;表达式的类型会自动提升或者转换为参与表达式求值的最上级类型 #include <stdio.h> int main() {int x -1;unsigned int y 2;if (x > y){printf("x is …

使用Express部署Vue项目

使用Express部署Vue项目 目录 1. 背景 2. 配置Vue CLI 1.1 安装nodejs 1.2 创建vue-cli 1.3 创建vue项目 1.4 构建vue项目3. 配置Express 2.1 安装express 2.2 创建项目4. 使用express部署vue项目 1&#xff0c;背景 我们想要做一个前后端分离的课程项目&#xff0c;前端…

nacos2.2.3最新版启动所遇到的问题总结

前言 有问题就看官方文档&#xff0c;看不懂或者还是报错再看博客&#xff01;因为有时候忙的焦头烂额&#xff0c;却发现官方写的非常清楚&#xff0c;而且人家还自带一个example示例&#xff0c;自己都没有看&#xff0c;自己瞎折腾&#xff01;本人吃过亏&#xff0c;特此提…

svo2论文

论文题目 SVO: Semidirect Visual Odometry for Monocular and Multicamera Systems 内容 1&#xff09; 具有最小特征漂移的长特征轨迹&#xff1b; 2&#xff09; 图像平面中的大量均匀分布的特征&#xff1b; 3&#xff09;新特征与旧地标的可靠关联&#xff08;即环路闭…

mybatisPlus主键策略解读

目录 主键生成策略介绍 AUTO策略 INPUT策略 ASSIGN_ID策略 NONE策略 ASSIGN_UUID策略 主键生成策略介绍 主键的作用就是唯一标识&#xff0c;我们可以通过这个唯一标识来定位到这条数据。当然对于表数据中的主键&#xff0c;我们可以自己设计生成规则&#xff0c;生成主键…

收藏与掘出 沉淀与成长

收藏从未停止&#xff0c;练习从未开始——掘出那些闪光的宝藏 目录 引言&#xff1a;收藏的深意分享一道你收藏的好题分享一个你收藏的便捷技巧积灰这么久&#xff0c;这个当时被你收藏的东西对现在的你还有用吗&#xff1f;结语&#xff1a;掘出 引言&#xff1a;收藏的深意 …

Python(二)

最怕你一生碌碌无为&#xff0c;还安慰自己平凡可贵。 --可以import 函数 变量 类啊 等等 -- 不一 一写了 大家自己写吧 -- 命名尽量不要使用大写或者中文 --比较运算符 又名 关系运算符 后面更新&#xff0c;请看后续 Python(二)

速度快\颜色准\功能多,移动端HEIF图片解码实现方案

HEIF图片压缩格式是一种使用HEVC编码技术存储图像数据的方式&#xff0c;在同等质量下相比JPEG可节省50%以上空间&#xff0c;无论是节约包体还是节省带宽&#xff0c;使用HEIF格式都能有所收益。 基于百度智能云音视频处理MCP的自研BD265编码器&#xff0c;百度智能云对象存储…

联想北京公司研发管理部高级经理周燕龙受邀为第十二届中国PMO大会演讲嘉宾

联想&#xff08;北京&#xff09;有限公司研发管理部高级经理周燕龙先生受邀为由PMO评论主办的2023第十二届中国PMO大会演讲嘉宾&#xff0c;演讲议题&#xff1a;PMO如何助力研发。大会将于8月12-13日在北京举办&#xff0c;敬请关注&#xff01; 议题简要&#xff1a; PMO在…