【C++初阶】类与对象(一)

目录

  • 1、初识面向对象思想
  • 2、类 struct
    • 2.1 C++中的struct及使用
  • 3、类 class
    • 3.1 类的定义
    • 3.2 类的访问限定符
      • 3.2.1 访问限定符是什么
      • 3.2.2 访问限定符的使用
      • 3.2.3 访问限定符的使用规范
      • 3.2.4 访问限定符与封装
    • 3.3 类做声明和定义分离
      • 3.3.1 声明和定义分离
      • 3.3.2 在函数声明的地方也可定义
    • 3.4 类的作用域
      • 3.4.1 类的成员变量命名规范
      • 3.4.1 类里支持重载
  • 4、类的实例化
    • 4.1 类的实例化是什么
    • 4.1 为什么有类的实例化
      • 4.1.1 类与对象的关系
  • 5、计算类的对象大小
    • 5.1 结构体内存对齐规则
    • 5.2 空类
    • 5.3 类对象存储方式
  • 6、关键字this指针
    • 6.1 this指针是什么
    • 6.2 this指针的特性
    • 6.3 练习
      • 6.3.1 题一
      • 6.3.2 题二

1、初识面向对象思想

C语言是面向过程的,将问题分解成多个步骤,通过函数调用一个一个解决。C++是在C语言的基础上增加了面向对象的思想,关注的是对象,将问题分成不同的对象,依靠对象之间交互完成的。

以洗衣服为例:
C语言: 洗衣服分为多个步骤逐一完成。
在这里插入图片描述
C++: 对象有人、衣服、洗衣粉和洗衣机,通过它们之间的交互完成。
在这里插入图片描述

2、类 struct

2.1 C++中的struct及使用

对于struct大家并不陌生,在C语言中它是用来定义一个结构体的关键字。如下表示栈:

typedef struct Stack
{
	int* a;
	int top;
	int capacity;
}Stack;
//声明
void StackPush(Stack* ps, int x);
void StackInit(Stack* ps);
//定义
void StackInit(Stack* ps)
{
	//...
}
void StackPush(Stack* ps, int x)
{
	//....
}

int main()
{
	Stack st;
	StackInit(&st);
	StackPush(&st, 1);
	return 0;
}

C++创始人对以上这种写法感觉很繁琐,所以对struct进行了升级,变成了类。struct定义的类名就是类型,且可以在类里定义函数。
如下:

struct Stack
{
	int* a;
	int top;
	int capacity;

	void Init()
	{
		//...
	}
	void Push(int x)
	{
		//....
	}
};

int main()
{
	Stack st;//类名就是类型,前面不需要加struct
	st.Init();
	st.Push(1);
	return 0;
}

这样写是不是比前面C语言的代码简洁了不少。当然,通常来说C++很少使用struct ,使用class比较多。

3、类 class

3.1 类的定义

class className
{
//
// 类体:由成员函数和成员变量组成
//
}; // 一定要注意后面的分号,与C语言的结构体同

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

3.2 类的访问限定符

3.2.1 访问限定符是什么

类一共有3个访问限定符,分别是:

public(公有)、protected(保护)、private(私有)

访问限定符是用来指定类中的成员或者函数是否可在类外被访问的关键字。它是C++实现封装的方式,同时让代码看起来更加完善。

3.2.2 访问限定符的使用

使用访问限定符先得知道它们的特性:

public(公有):修饰的成员在类外可以直接被访问
protected(保护) 和private(私有):在类外不能直接被访问。(在这里protected和private的功能是类似的)
访问限定符的作用域从当前开始到出现下一个访问限定符结束,如果后面没有其他访问限定符,类结束就是该访问限定符的作用域结束

struct Stack
{
private://私有
	int* a;
	int top;
	int capacity;
public://公有
	void Init()
	{
		//...
	}
	void Push(int x)
	{
		//....
	}
};
int main()
{
	Stack st;
	st.Init();
	st.Push(1);
	return 0;
}

类里面的成员函数是公有的,所以类外可以访问到;成员变量是私有的,类外不能被访问。
在这里插入图片描述

3.2.3 访问限定符的使用规范

一般情况下,我们看到的C++代码中的类里面都有访问限定符,比如private和public。大家可能有一个疑惑,访问限定符非写不可吗?其实不是。不写访问限定符也是有区别的,只不过这个区别是在class和struct两个关键字上。class不写访问限定符,默认的访问权限是private;struct不写访问限定符,默认的访问权限是public。这同时也说明了为什么使用struct时类外都可以访问到里面的成员,因为C++要兼容C语言。但是,为了防止错误发生,尽量写访问限定符,这样同时也让代码比较规范。

3.2.4 访问限定符与封装

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

就好比用户使用电脑,只要操作键盘、鼠标和开关机即可,不必知道电脑内部是如何设计的。

3.3 类做声明和定义分离

3.3.1 声明和定义分离

类的定义有两种写法,一种是声明和定义全部写在类里,另一种是分文件声明和定义分离,在做项目的时候或者代码规模较大以后者居多。

#include <iostream>
using namespace std;
class Stack
{
private:
	int* a;
	int top;
	int capacity;
public:
	void Init();
	void Push(int x);
};
/
void Stack::Init()
{
	//...
}
void Stack::Push(int x)
{
	//...
}
/
int main()
{
	Stack st;
	st.Init();
	st.Push(1);
	return 0;
}

定义函数的文件里函数名加上Stack:: 是为了找到Stack类里的函数,因为如果头文件里不止有栈的初始化的声明,可能也有其他的,比如队列的初始化的声明。

3.3.2 在函数声明的地方也可定义

C++是可以声明和定义混着写的
比如:

class Stack
{
private:
	int* a;
	int top;
	int capacity;
public:
	void Init();
	void Push(int x)
	{
		//...
	}
};

要注意的一点,定义写在声明里函数变成了内联函数。但是也并不完全是内联函数,因为是否是内联函数取决于编译器。一般来说,声明里定义函数最好是代码比较少的,少的写在声明里可以变成内联函数,提高程序运行效率。

3.4 类的作用域

3.4.1 类的成员变量命名规范

我们在写类的成员变量命名时,要注意规范性。
如下:

class Date
{
private:
	int year;
	int month;
	int day;
public:
	void Init(int year, int month, int day)
	{
		year = year;
		month = month;
		day = day;
	}
	void Print()
	{
		cout << year << "-" << month << "-" << day << endl;
	}
};

成员变量分别是year、month、day,但是传进来的参数的名字也是year、month、day,赋值的时候是不是感觉很乱。所以建议这样写更好:

class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
};
int main()
{
	Date d1;
	d1.Init(2023, 10, 23);
	d1.Print();
	return 0;
}

在这里插入图片描述
成员变量前面加个下划线就好区分了。这里不一定是前面加下划线,后面加或者其他方式都行,只要能够区别参数的名字即可。

3.4.1 类里支持重载

一个类就是一个作用域,是支持重载的

class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	void Init()
	{
		_year = 2020;
		_month = 1;
		_day = 1;
	}
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
};
int main()
{
	Date d1;
	d1.Init();
	d1.Print();
	return 0;
}

在这里插入图片描述

4、类的实例化

4.1 类的实例化是什么

类创建对象的过程就叫做类的实例化。类是对对象进行描述的,它限定了类里面有哪些成员,定义出一个类并没有分配实际的内存空间来存储它。

    Date d1;
// 类 + 对象

4.1 为什么有类的实例化

类里面的成员变量是声明,它没有实际的空间,不能存储数据,必须先要实例化才行。

4.1.1 类与对象的关系

一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量。类可以比作一张建筑物的图纸,对象就是具体的建筑物,图纸总不能居住吧,但是具体的建筑可以。
在这里插入图片描述

5、计算类的对象大小

5.1 结构体内存对齐规则

计算类的对象大小之前,我们先来回顾下C语言学过的内存对齐规则:

1.第一个成员在与结构体偏移量为0的地址
2.某个成员变量的对齐数 = 编译器默认的对齐数 和 该成员变量大小 中的较小值
3.前面的成员变量的对齐数要是当前成员变量的对齐数的整数倍
4.结构体总大小 = 最大对齐数的整数倍
5.如果结构体嵌套结构体,里面的结构体还是以上步骤进行。外面的结构体也是一样的,只不过整体的总大小是最大对齐数的整数倍,最大对齐数可能是嵌套的结构体的大小。

class A
{
	int year;
	int month;
	int day;
};
class B
{
	char y;
	int t;
};
int main()
{
	A a;
	B b;
	cout << sizeof(a) << endl;
	cout << sizeof(b) << endl;
	return 0;
}

在这里插入图片描述

5.2 空类

空类是没有成员变量的类
比如:

class A
{

};
int main()
{
	A a;
	cout << sizeof(a) << endl;
	return 0;
}

a的大小是:
在这里插入图片描述
无成员变量的类编译器给一个字节,只是标记它的对象存在而已。

5.3 类对象存储方式

先来看一个例子:

class A
{

};
class B
{
	void Func()
	{

	}
};
int main()
{
	A a;
	B b;
	cout << sizeof(a) << endl;
	cout << sizeof(b) << endl;
	return 0;
}

运行结果:
在这里插入图片描述
我们知道,只要是无成员变量的类都是给一个字节,但是为什么有成员函数却不算进去呢?这与类对象的存储方式有关。

每个对象的成员变量是不同的,但是调用同一个函数。如果类创建多个对象,那么每个对象都要保存一份相同的代码,这样就会浪费空间。所以对象里面只保存成员变量的代码,成员函数放在公共代码段。就好比一间教室有50个学生,每个学生有自己的课本和笔,但是总不能给每个学生安排一个老师吧。

6、关键字this指针

6.1 this指针是什么

先来看一段代码:

class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	void Func(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
};
int main()
{
	Date d1;
	Date d2;
	d1.Func(2023, 10, 23);
	d1.Print();
	d2.Func(2020, 1, 3);
	d2.Print();
	return 0;
}

运行结果:
在这里插入图片描述
这里设置了两个对象d1和d2,它们调用的是同一个成员函数,问题是函数是怎么知道应该使用哪个对象(怎么找到对象的)

C++引入了this指针解决了这个问题,this指针是一个成员函数隐藏的形参,对象在调用成员函数时,将对象的地址传给this指针,对成员变量的操作都需要该指针访问。只不过不需要用户自己写出来,C++编译器自动完成。

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

6.2 this指针的特性

  1. this指针的类型:类类型* const
  2. 只能在“成员函数”的内部使用

this指针存在哪里
this指针是一个形参,存在栈上,但是VS存在ecx寄存器下,不同的编译器可能不同。

this指针可以为空吗
可以为空,但是有些情况不能为空。调用函数时如果函数内部不需要通过this指针指向当前对象且对其进行操作时可以为空,否则会报错。

6.3 练习

6.3.1 题一

该程序运行结果是~~

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

答案:正常运行
在这里插入图片描述
分析:
p是空指针,很多人以为是空指针然后加箭头就是运行错误,这里不是。Print函数不在对象里,p的作用首先是创建对象,然后检查Print这个成员函数是否在这个类里面。没有,运行错误;反之,正常运行。

6.3.2 题二

该程序运行结果是~~

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

答案:运行崩溃
分析:
这题与上题的代码基本类似,只不过成员函数里要打印的是 _a ,p是空指针,传参给this指针也是空指针,打印 _a 不就对空指针解引用了吗,会导致程序运行崩溃。

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

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

相关文章

升级 Xcode 15模拟器 iOS 17.0 Simulator(21A328) 下载失败

升级 IDE Xcode 15 后本地模拟器 Simulator 全被清空,反复重新尝试 Get 下载频频因网络异常断开而导致失败 ... 注:通过 Get 方式下载一定要保证当前网络环境足够平稳,网络环境不好的情况下该方法几乎成不了 解决办法 Get 方式行不通可以尝试通过 官网 途径先下载 模拟器安装包…

移动端之Unity嵌入Android项目开发

目录 前言1 搭建开发环境2 创建Unity项目 2.1 新建项目2.2 Unity构建配置2.3 Android环境相关配置2.4 导出Unity库文件3 创建Android项目 3.1 新建Android项目3.2 Android环境相关配置3.2 导入Unity相关的库3.3 Android中跳转到Unity视图4 进阶扩展 4.1 包体积优化 4.1.1 mono…

HarmonyOS第一课运行Hello World

前言 俗话说&#xff0c;工欲善其事必先利其器。鸿蒙第一课&#xff0c;我们先从简单的Hello World运行说起。要先运行Hello World&#xff0c;那么我们必须搭建HarmonyOS的开发环境。 下载与安装DevEco Studio 在HarmonyOS应用开发学习之前&#xff0c;需要进行一些准备工作&a…

<蓝桥杯软件赛>零基础备赛20周--第2周

报名明年4月蓝桥杯软件赛的同学们&#xff0c;如果你是大一零基础&#xff0c;目前懵懂中&#xff0c;不知该怎么办&#xff0c;可以看看本博客系列&#xff1a;备赛20周合集 20周的完整安排请点击&#xff1a;20周计划 每周发1个博客&#xff0c;共20周&#xff08;读者可以按…

【pwn入门】使用python打二进制

声明 本文是B站你想有多PWN学习的笔记&#xff0c;包含一些视频外的扩展知识。 程序网络交互初体验 将程序部署成可以远程访问的 socat tcp-l:8877,fork exec:./question_1_plus_x64,reuseaddr通过网络访问程序 nc 127.0.0.1 8877攻击脚本 import socket import telnetli…

带你深入了解队列(c/cpp双版本模拟实现)

目录 一.队列的概念及结构 二.队列的实现 2.1队列的结构 2.2初始化队列 2.3队尾入队列 2.4队头出队列 2.5获取队列头部元素 2.6获取队列队尾元素 2.7获取队列中有效元素个数 2.8检测队列是否为空 2.9销毁队列 三.C 版本模拟实现队列 一.队列的概念及结构 队列…

基于 C# 实现样式与数据分离的打印方案

对于八月份的印象&#xff0c;我发现大部分都留给了出差。而九月初出差回来&#xff0c;我便立马投入了新项目的研发工作。因此&#xff0c;无论是中秋节还是国庆节&#xff0c;在这一连串忙碌的日子里&#xff0c;无不充满着仓促的气息。王北洛说&#xff0c;“活着不就是仓促…

Android Glide限定onlyRetrieveFromCache取内存缓存submit超时阻塞方式,Kotlin

Android Glide限定onlyRetrieveFromCache取内存缓存submit超时阻塞方式,Kotlin import android.os.Bundle import android.util.Log import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope import com.b…

剑指JUC原理-4.共享资源和线程安全性

共享问题 小故事 老王&#xff08;操作系统&#xff09;有一个功能强大的算盘&#xff08;CPU&#xff09;&#xff0c;现在想把它租出去&#xff0c;赚一点外快 小南、小女&#xff08;线程&#xff09;来使用这个算盘来进行一些计算&#xff0c;并按照时间给老王支付费用 …

强化学习------PPO算法

目录 简介一、PPO原理1、由On-policy 转化为Off-policy2、Importance Sampling&#xff08;重要性采样&#xff09;3、off-policy下的梯度公式推导 二、PPO算法两种形式1、PPO-Penalty2、PPO-Clip 三、PPO算法实战四、参考 简介 PPO 算法之所以被提出&#xff0c;根本原因在于…

按照正规的软件开发流程,项目原型评审是全程对着页面评审吗

项目原型评审是软件开发过程中的一步&#xff0c;它的目的是确保设计和需求的一致性&#xff0c;以及提供一个可视化的界面供所有相关方进行沟通和理解。评审过程中&#xff0c;可能会涉及到多个方面&#xff1a; 用户界面&#xff08;UI&#xff09;&#xff1a;确保UI设计满足…

电脑如何激活windows

当我们电脑出现如下图&#xff1a; 显示需要激活windows时&#xff0c;操作如下。 1、桌面-新建-文本文档 2、将文档命名为&#xff08;激活windows.bat&#xff09;把原有文档中的后缀.txt去掉 3、点击右键&#xff0c;选择编辑输入代码 slmgr/skms kms.03k.org slmgr/ato4、…

Python----break关键字对while...else结构的影响

案例&#xff1a; 女朋友生气&#xff0c;要求道歉5遍&#xff1a;老婆大人&#xff0c;我错了。道歉到第三遍的时候&#xff0c;媳妇埋怨这一遍说的不真诚&#xff0c;是不是就是要退出循环了&#xff1f;这个退出有两种可能性&#xff1a; ① 更生气&#xff0c;不打算原谅…

c语言进制的转换8进制转换2进制

c语言进制的转换之8进制转换2进制与2转8 c语言的进制的转换 c语言进制的转换之8进制转换2进制与2转8一、八四二一法则二、八进制转换二进制方法三、八进制程序打印 一、八四二一法则 二、八进制转换二进制方法 如&#xff1a;3703转换为2进制 按照八四二一法则&#xff0c;分为…

创纪录的1亿RPS DDoS攻击利用HTTP/2快速重置漏洞

导语&#xff1a;最近&#xff0c;一项创纪录的DDoS攻击引起了广泛关注。攻击者利用了HTTP/2协议中的一个快速重置漏洞&#xff0c;发起了一系列超大规模的攻击。本文将为大家详细介绍这次攻击的背景、影响以及应对措施。 攻击背景 最近&#xff0c;全球范围内遭受了一系列规模…

Fabric.js 样式不更新怎么办?

本文简介 带尬猴&#xff0c;我嗨德育处主任 不知道你有没有遇到过在使用 Fabric.js 时无意中一些骚操作修改了元素的样式&#xff0c;但刷新画布却没更新元素样式&#xff1f; 如果你也遇到同样的问题的话&#xff0c;可以尝试使用本文的方法。 是否需要重新绘制 我先举个例…

【Javascript】ajax(阿甲克斯)

目录 什么是ajax? 同步与异步 原理 注意 写一个ajax请求 创建ajax对象 设置请求方式和地址 发送请求 设置响应HTTP请求状态变化的函数 什么是ajax? 是基于javascript的一种用于创建快速动态网页的技术&#xff0c;是一种在无需重新加载整个网页的情况下&#xff0c…

解决Visual studio 未能正确加载...包问题

问题 解决&#xff1a; 菜单: Visual Studio 2019 -> 输入"devenv /resetsettings " 将之前的设置恢复到原始状态。且可以正常使用。理论应该可以使用到其它版本中……

业务架构、应用架构、技术架构、数据架构

架构规划的重要性 如果没有进行合理的架构规划&#xff0c;将会引发一系列的问题。为了避免这些问题的发生&#xff0c;企业需要进行业务架构、应用架构、技术架构和数据架构的全面规划和设计&#xff0c;以构建一个清晰、可持续发展的企业架构。 https://www.zhihu.com/que…

CVE-2022-32991靶场复现

靶场环境&#xff1a; 题目提示了该CMS的welcome.php中存在SQL注入攻击。 CVE官方给出的提示&#xff1a; welcome.php页面存在SQL注入&#xff0c;并且这个参数是eid 打开靶场环境&#xff1a; 页面是一个登陆注册的界面 用户注册&#xff1a; 1 010.com 123456 123456 点击Re…