初识C++ · 类和对象(上)

目录

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

2.类的引入

3.类的定义

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

4.1 访问限定符

4.2 封装

5.类的作用域

6.类的实例化

7.类的对象大小的计算

8.类成员函数的this指针


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

C语言是一门面向过程的语言,注重的是解决问题的过程,举个例子——洗衣服,洗衣服的过程是:拿衣服——放进洗衣机——放洗衣液——放水——打开洗衣机——开始工作——结束——晾衣服,注重整个过程,C++是一门面向对象的语言,注重的是解决问题时候涉及的对象,比如洗衣服的时候涉及到了衣服,洗衣机,水,洗衣液等对象,注重的是对象交互来解决某个问题。


2.类的引入

在C语言中的结构体里面只能包含变量,但是在C++里面觉得结构体里面只能放变量不太方便,于是规定函数也可以放在里面:

struct St
{
	int a;
	int b;
	int c;
	int Add(int x,int y)
	{
		return x + y;
	}
};
int main()
{
	struct St s1;
	cout << s1.Add(1, 2);
	return 0;
}

而在中C++更喜欢使用class来定义类,那么这样有有什么好处呢?
比如我们写个栈的初始化,C语言的写法是定义好之后创建函数传参传栈的指针进去,再进行初始化,C++不用:

class Stack
{
	void StackInit()
	{
		arr = nullptr;
		size = capacity = 0;
	}

	int* arr;
	int size;
	int capacity;

};

如此,直接进行初始化就可以了。


3.类的定义

类的定义使用的关键字是class,定义的时候和结构体一样,花括号后面都有一个分号,class后面跟的就是类名:

class ClassName
{

	//...
};

其中,类体(花括号)里面的变量叫做类的属性或者是成员变量,类体里面的函数叫做类的方法或者成员函数

类中定义函数的时候有两种方法:
一是定义和声明放在一起,如:

class ClassName
{
	int Sub(int ,int )
	{
		return 1;
	}
	//...
};

二是声明和定义放在对应的文件里面,如:

//Stack.h

class Stack
{
public:
	void Print();

private:
	int* arr;
	int size;
	int capacity;

};
//Stack.cpp

void Stack::Print()
{
	cout << "Print" << endl;
}

int main()
{
	Stack s1;
	s1.Print();
	return 0;
}

这里有两个需要注意的地方,一个是头文件里面的public和private,一个是.cpp里面的函数定义。

当定义和声明分离的时候,定义函数的时候要在函数名前面加上类名和两个冒号,这是代表去这个类域里面访问这个函数。

定义变量的时候:class Stack s1 和 Stack s1都是可以的,那么方便起见我们就不写class,反正有没有都是可以的。

一般建议的还是第二种,声明和定义分开的形式,这样代码可读性更大,简洁度更高,其次就是函数命名的一些建议

class Date
{
public: 
	void Init(int year,int month,int minute)
	{
		_year = year;
		_month = month;
		_minute = minute;
	}
private:
	int _year;
	int _month;
	int _minute;
};

命名类里面的变量的时候大多数情况下加一个下划线_,这是因为如上情况的时候避免混淆,初始化的时候,如果变量也是year,那么就是year = year,代码的可读性就非常差,那有人说把参数的year换成y不就行了?

实际上参数的命名最好要有比较明显的实际意义,取名为y这谁知道参数对应的含义是什么呢?


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

4.1 访问限定符

上文还有一个注意事项没有介绍,就是public和private的介绍,这是C++中定义类的访问限定符,顾名思义,就是访问变量的时候访问是否会受到限制。

那么这有什么意义呢?

就好比文物展览,如果没有保安,没有文物警戒线等,文物是不大能保存完整的,这时候“访问限定符”就起作用了,可以隔绝一部分没有素质的人。

代码也是如此,C语言中结构体定义好之后就是谁都可以访问它,所以比较考验程序员的素质,C++不一样,C++加了访问限定符,有些变量就不能被随意访问,就起到了一定的保护作用。

默认的是用private修饰,也就是默认是私有的,不让访问。

访问限定符一共有3个,private,public,protected:

关于访问限定符的说明:
1 private和protected在外部不能直接被访问,public可以直接被访问

2 访问限定符的作用域是从该限定符开始一直到下一个限定符出现中的区域

3 class默认限定符为private,struct默认限定符为public(因为要兼容C语言)

4.2 封装

面向对象有三大特性:封装,继承,多态

封装就是利用了访问限定符,数据和使用数据的方法的结合,达到隐藏对象的具体细节和属性,但是可以通过接口的实现和外界进行交互。

封装其实就是管理,让使用者使用该使用的,不该使用的就是不使用,比如电脑,电脑的内部CPU,GPU怎么工作的我们是不用关心,我们关心的是我们使用电脑可以完成什么样的工作,这就是一种封装。


5.类的作用域

类定义好了之后就是单独创建了一个域,这个域叫做类域,和命名空间一样,我们使用里面的成员的时候我们需要指定一下是哪个地方的,比如:

class calculator
{
public :
	int Add(int ,int);
};

int calculator::Add(int x, int y)
{
	return x + y;
}
int main()
{
	calculator c1;
	cout << c1.Add(2,4) << endl;
	return 0;
}

目前我们学了 局部域全局域命名空间域类域,要注意区分。


6.类的实例化

类就是一个模型,对象就是根据这个模型创建出来的,所以类和对象常常放在一起,类的实例化我们就可以理解为:我们有一张设计图纸,我们根据设计图纸创建了一些东西,这个过程就叫做实例化

只有实例化出来的对象才可以"存放东西",因为类是不能存放数据的,图纸怎么存放数据?是吧。

class calculator
{
public :
	int Add(int ,int);
private:
	int _a;
};

int main()
{
	calculator _a = 1;
	return 0;
}

如果直接给数据就会报错了。

那么提个问题:

类里面的成员变量是声明还是定义?

答案是声明,也就是说在类里面告诉了你有这个东西,但是我还没有给它开辟空间,这也是实例化之前不能放数据的一个理由。

那么什么时候是定义呢?

实例化的时候我们开辟了空间,这个时候就是定义,因为我们开辟了空间,可以用来存放数据了。


7.类的对象大小的计算

我们来看下面五个类:

class C1
{
public:
	int Add();
};
class C2
{
private:
	int _a;
};
class C3
{

};
class C4
{
	char c;
};
class C5
{
public:
	int Add();
	char c;
};

试问三个类的大小是多大?

第一个类有函数,第二个类有一个int,第三个类是空类。

问题在于:函数算不算进去?

因为类是结构体演化而来的,所以计算的是套用结构体的内存对齐。

空类的内存是1,这是因为空类里面没有东西,为了存这个类的地址,所以必须开辟一个字节用来保存它的地址,但是不存储数据。

C2 和 C3对比就知道,一个是4字节,一个是1字节,因为一个是int,一个是char。

但是C1和C3对比,都是一个字节,但是函数的内存不可能是一字节,所以C1实际上也是空类,这里结合C5和C1也知道函数是不占内存空间的。

所以计算类的大小的时候函数不算进大小,其次就是空类的大小是1,这个1是用来存储类的地址的,其余就是结构体的内存对齐了。


8.类成员函数的this指针

class Cl
{
public:
	int Add(int ,int);
private:
	int _a;
	char _c;
};
int Cl::Add(int x,int y)
{
	return x + y;
}
int main()
{
	Cl c1;
	int ret1 = c1.Add(1, 3);
	Cl c2;
	int ret2 = c2.Add(2, 5);
	return 0;
}

先来看这样一段代码,提问:调用的函数是一个函数吗?还是说调用的是不同对象中的函数?

如果调用的是不同的函数,也就是内存为函数开辟了两个函数栈帧,那么汇编代码中的反汇编call对应的函数的地址应该是不同的,那么:

在汇编代码中,对应的地址是一样的,说明调用的函数是一个,这也可以解释前面的为什么空类里面有一个函数的时候对应的大小却是1,因为函数放在的区域是公共代码段,所以计算大小的时候函数的大小不算在里面。

那么问题来了,函数只有一个,系统是怎么使用函数的,一个函数怎么应用不同的参数呢?

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

void Cl::Print(int x)
{
	cout << x << endl;
}
void Cl::Print(Cl* this,int x)
{
	cout << this->x << endl;
}

就像如此,通过指针来实现各种操作,也就达到了一个函数多个对象使用的目的。

但是this指针实现的时候都是透明的,我们不需要手动参与,编译器会自己实现。

下面来看两个关于this指针的题目:

class A
{
public :
	void Print()
	{
		cout << "Print()" << endl;
	}
private:
	int _a;
};

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

int main()
{
	A* p = nullptr;
	p->Print();
	return 0;
}

问:编译结果是否正常?

第一个代码的编译结果是正常的,虽然类指针是空指针,但是访问的不是成员变量,那么实际上的空指针就不会用上,那么this指针是空指针是没问题的,没用到空指针程序就不会崩溃。

第二个代码就会崩溃,因为this指针接收了传过来的空指针,我们又用到了这个空指针访问成员变量,程序就会崩溃了。

	void Print()
	{
		cout << this << endl;
		//cout << _a << endl;
	}

this指针打印出来就是空的。


感谢阅读!

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

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

相关文章

FPGA(Verilog)实现按键消抖

实现按键消抖功能&#xff1a; 1.滤除按键按下时的噪声和松开时的噪声信号。 2.获取已消抖的按键按下的标志信号。 3.实现已消抖的按键的连续功能。 Verilog实现 模块端口 key_filter(input wire clk ,input wire rst_n ,input wire key_in , //按下按键时为0output …

《QT实用小工具·二十二》多种样式导航按钮控件

1、概述 源码放在文章末尾 该项目实现了多种样式的导航按钮控件 可设置文字的左侧、右侧、顶部、底部间隔。 可设置文字对齐方式。 可设置显示倒三角、倒三角边长、倒三角位置、倒三角颜色。 可设置显示图标、图标间隔、图标尺寸、正常状态图标、悬停状态图标、选中状态图标…

纯C语言手搓GPT-2,前OpenAI、特斯拉高管新项目火了

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了免费的人工智能中文站https://ai.weoknow.com 新建了收费的人工智能中文站https://ai.hzytsoft.cn/ 更多资源欢迎关注 「Real men program in C.」 众所周知&#xff0c;大语言模型还在快速发展&#xff0c;应该有…

自动驾驶基础技术-无迹卡尔曼滤波UKF

自动驾驶基础技术-无迹卡尔曼滤波UKF Unscented Kalman Filter是解决非线性卡尔曼滤波的另一种思路&#xff0c;它利用Unscented Transform来解决概率分布非线性变换的问题。UnScented Kalman Filter不需要像Extended Kalman Filter一样计算Jacobin矩阵&#xff0c;在计算量大…

Vue - 你知道Vue2中对象动态新增属性,视图无法更新的原因吗

难度级别:中高级及以上 提问概率:55% 这道题面试官会这样描述,比如有这样一个场景,一个对象里有name属性,可以正常显示在页面中。但后续动态添加了一个age属性,通过调试打印发现对象里的age属性已经添加了上了,但试图中却没有展示出来,…

程序语言基础

根据希赛相关视频课程汇总整理而成&#xff0c;个人笔记&#xff0c;仅供参考。考点偏向于通用程序语言的基础概念。 程序语言基础概念 程序设计语言&#xff1a; ①低级语言 机器语言汇编语言 汇编语言&#xff1a;指令语句/伪指令语句/宏指令语句 ②高级语言 Fotrane语言&…

计算系数(acwing,数论)

题目描述&#xff1a; 给定一个多项式 (axby)^k&#xff0c;请求出多项式展开后 x^n*y^m 项的系数。 输入格式&#xff1a; 共一行&#xff0c;包含 5 个整数&#xff0c;分别为 a&#xff0c;b&#xff0c;k&#xff0c;n&#xff0c;m&#xff0c;每两个整数之间用一个空格…

2024马来西亚电商选品博览会

2024马来西亚电商选品博览会 展会概况 展会名称&#xff1a;2024马来西亚电商选品博览会 主办单位&#xff1a;广东进出口商会 时间:2024.11.29-12.1 地点&#xff1a;马来西亚国际贸易展览中心(MITEC) 展览面积&#xff1a;10000平方米 展会简介 2024马来西亚跨境电商选…

vue2 父子组件通讯

父传子 父组件&#xff1a;app.vue <template><div>app 父组件<!-- 2.动态绑定定义的数据 --><LiCount :title"mytitle"></LiCount></div> </template><script> import LiCount from "./components/LiCount.…

自定义的@TableField,解决插入或更新失效

自定义的TableField&#xff0c;解决插入或更新失效 01 使用场景 需要使用到mybatisplus的自动填充时 02 配置类和扫描包 在启动类处配置扫描包&#xff0c;每次启动扫描配置类 配置类 package com.ruoyi.framework.hander;import com.baomidou.mybatisplus.core.handlers.…

c# wpf LiveCharts 饼图 简单试验

1.概要 c# wpf LiveCharts 饼图 简单试验 2.代码 <Window x:Class"WpfApp3.Window5"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://schem…

YOLOv9改进策略 :小目标 | 新颖的多尺度前馈网络(MSFN) | 2024年4月最新成果

💡💡💡本文独家改进:多尺度前馈网络(MSFN),通过提取不同尺度的特征来增强特征提取能力,2024年最新的改进思路 💡💡💡创新点:多尺度前馈网络创新十足,抢先使用 💡💡💡如何跟YOLOv8结合:1)放在backbone后增强对全局和局部特征的提取能力;2)放在detect…

在Spring中使用Redis

端口怎么设置&#xff0c;看我前一篇文章 前面使用jedis&#xff0c;通过Jedis对象中各种方法来操作redis的。 此处Spring中则是通过StringRedisTemplate来操作redis。 最原始提供的类是RedisTemplate StringRedisTemplate是RedisTemplate的子类&#xff0c;专门处理文本数据的…

网格矢量如何计算莫兰指数

网格矢量如何计算莫兰指数 引言 遇到一个问题&#xff0c;计算矢量网格的莫兰指数。 概念解释 莫兰指数 莫兰指数&#xff08;Moran’s Index&#xff09;是一种空间自相关指标&#xff0c;用于衡量空间数据的相似性和聚集程度。它可以用来描述一个区域与其邻近区域之间的属…

LeetCode 使数组连续的最少操作数

地址&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 难度&#xff1a;困难 题目描述&#xff1a;给你一个整数数组 nums 。每一次操作中&#xff0c;你可以将 nums 中 任意 一个元素替换成 **任意 **整数。 如果 nums 满足以下条件&#xff0c;那么它是 连续的 &#x…

java 错误:Illegal key size

前言 jdk 1.8 错误&#xff1a;Illegal key size Illegal key size分析 这个是由于jdk限制策略&#xff0c;导致只能128位key进行加解密&#xff0c;而256位加解密则抛出异常。 解决办法 下载jar&#xff1a;jar 下载地址 解压 替换%JAVA_HOME%/\jre\lib\security目录下…

专题十二、字符串

字符串 1. 字符串字面量1.1 字符串字面量中的转义序列1.2 延续字符串字面量1.3 如何存储字符串字面量1.4 字符串字面量的操作1.5 字符串字面量与字符常量 2. 字符串变量2.1 初始化字符串变量2.2 字符数组与字符指针 3. 字符串的读和写3.1 用 printf 函数和 puts 函数写字符串3.…

猫头虎分享已解决Error: 成功解决“No module named ‘sklearn‘ (ModuleNotFoundError)“

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 文章目录 猫头虎分享已解决Error: 成功解决"No module named sklearn (ModuleNotFoundError)" &#x1f431;&#x1f989;&#x1f527;摘要正文内容 介绍错误原因分析…

R-Tree的简单介绍

一、R-Tree简介 R-Tree&#xff0c;全称是“Real Tree”&#xff0c;是一种专门为处理多维空间数据&#xff08;尤其是二维空间数据&#xff0c;如地理坐标&#xff09;设计的树形数据结构。 简单来说&#xff0c;它就像是一个特殊的目录&#xff0c;将空间数据按照它们的位置…

【C语言】扫雷小游戏

文章目录 前言一、游戏玩法二、创建文件test.c文件menu()——打印菜单game()——调用功能函数&#xff0c;游戏的实现main()主函数 game.c文件初始化棋盘打印棋盘随机布置雷的位置统计周围雷的个数展开周围一片没有雷的区域计算已排查位置的个数排查雷(包括检测输赢): game.h文…