[C++] 类与对象(上)

news2024/9/22 5:34:05

目录

1、前言

2、类的引入

3、类的定义

3.1 类的两种定义方式

4、类的访问限定符

5、类的作用域

6、类的实例化

7、类对象模型

7.1 内存对齐规则

7.1 类对象的存储方式

8、this指针

8.1 this指针的特性

8.2 this指针是否可以为空


1、前言

C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。

我们来举列子说明一下:

比如蒸米饭这件事,C语言关注的就是,淘米 -> 加水 -> 加米 -> 起锅 -> 烧锅 -> 水沸腾 -> 放入盛有生米的盆 -> 烧锅 -> 拿出;C++在这里关注的是对象:人、米、水、电饭煲,人不需要知道电饭煲如果工作的,只需要四个对象之间交互完成任务就可以。

2、类的引入

C语言结构体(struct)中只能定义变量。

而在C++中,新增加了玩法,结构体内不仅可以定义变量,也可以定义函数

举例:

struct Person
{
	void personInit()
	{
		cout << "void personInit()" << endl;
	}

	int _age;
	char _name[20];
};
int main()
{
	Person p;
	p.personInit();

	return 0;
}

上面结构体的定义,在C++中更喜欢用class来代替。

3、类的定义

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

class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。
类体中内容称为类的成员:类中的变量称为类的属性成员变量; 类中的函数称为类的方法或者成员函数

3.1 类的两种定义方式

3.1.1 声明和定义全部放在类体中

需注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。

class Person
{
public:
	void PersonInit()
	{
		cout << "void PersonInit()" << endl;
	}

private:
	int _age;
	char _name[20];
};

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

//Person.h
class Person
{
public:
	void PersonInit();

private:
	int _age;
	char _name[20];
};


//Person.c
#include "Person.h"
void Person::PersonInit()
{
	cout << "void Person::PersonInit()" << endl;
}

一般情况下,更期望采用第二种方式。

注意:一般在声明成员变量的时候成员变量前加_(下划线),为区分成员函数的形参(只要能区分就可以,前家下划线是我的一种方式,谷歌C++规范一般喜欢后加_)。

如果成员变量没有特殊标记时,当在成员函数内用到成员变量,并为其赋值的时候,函数会采用局部优先的原理,将自己赋给自己,这样就达不到预期的效果。

4、类的访问限定符

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

【访问限定符说明】

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

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

5、类的作用域

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

class Person
{
public:
	void PersonInit();//声明

private:
	int _age;
	char _name[20];
};


//初始化定义的时候函数名前加 Person::,表明PersonInit是Person类域的
void Person::PersonInit()//定义
{
	cout << "void Person::PersonInit()" << endl;
}

6、类的实例化

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

在实例化之前,定义出来的类是不占空间的。
例如:

class Person
{
public:
    void PersonInit()
    {
        cout << "void PersonInit()" << endl;
    }

private:
    int _age;		//声明
    char _name[20];
};

int main()
{
	Person::_age = 20;
    //定义开空间
    Person p;

    return 0;
}

在没有实例化p的时候,定义的 class Person 类不会开辟空间。

实例化出了 p 对象,这时 p 是占用实际的空间的,存储了成员变量。(这里实例化后,p也不能直接用_age,因为是私有的,后面会讲如何赋值,打印)。

类就像是设计图。

类实例化出对象就像现实中使用设计图盖房子。

在没有盖房子之前,这块区域没有占空间。

实例化就是按图盖房子,这时就占用了空间。

我们可以使用设计图盖多个房子,这些房子都占空间

7、类对象模型

如何计算类对象的大小呢?

例如:

class A
{
public:
	void AInit(int a, int b)
	{
		cout << "void AInit(int a, int b)" << endl;
	}

private:
	int _a;
	int _b;
};
class B
{

private:
	int _a;
	int _b;
};
class C
{};
int main()
{
	cout << "类A的大小:" << sizeof(A) << endl;
	cout << "类B的大小:" << sizeof(B) << endl;
	cout << "类C的大小:" << sizeof(C) << endl;

	return 0;
}

运行结果:

总结:

1、成员函数不算在类的大小中;

2、类的大小只与成员变量有关,并遵循结构体对齐规则;

3、空类的大小为1字节(不存储数据,只是占位,表示对象存在过)。

7.1 内存对齐规则

1. 第一个成员在与结构体偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的对齐数为8
3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

7.1 类对象的存储方式

为什么类中的成员函数不占空间?那成员函数是存在哪里呢?

实例化后的类中只存储类成员变量

成员函数保存在公共的代码段。

我们画图来理解一下:

8、this指针

我们这里写一个日期类来看一下:

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

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

int main()
{
	Date d1;
	d1.Init(2023, 7, 30);

	return 0;
}

这里看似 Init 函数只有三个形参

调用的时候传了三个参数

实际上,这里还隐含了一个 this 指针。

我们这里画图来看一下:

这里对成员变量赋值的时候,前后都会加一个 this-> 来接引用访问。

8.1 this指针的特性

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

因此,我们写的时候就不可以这样写:

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

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

int main()
{
	Date d1;
	d1.Init(&d1, 2023, 7, 30);

	return 0;
}

this指针隐含着,如果我们自己加上就是错误的。

this在实参和形参的位置上不能显示写

但是在类里面可以显示的用

如下:

class Date
{
public:
    //this在实参和形参的位置上不能显示写
    //但是在类里面可以显示的用
	void Init(int year, int month, int day)
	{
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}

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

int main()
{
	Date d1;
	d1.Init(2023, 7, 30);

	return 0;
}

8.2 this指针是否可以为空

我们来看下面几段代码:

class Person
{
public:
	void PersonInit()
	{
		cout << "void PersonInit()" << endl;
	}

private:
	int _age;		//声明
	char _name[20];
};

int main()
{
	Person* p = nullptr; //初始化为空指针
	p->PersonInit();

    return 0;
}

运行结果:

这里我们就会产生疑问,p是空指针为什么可以解引用呢?还是正常运行。

这里对于函数定义在类里面且短小,编译器会当作内联函数,直接展开,并不会解引用;

而如果声明与定义分离或者编译器不将其当作内联函数,就是call Init函数(调用函数)的地址,也不是解引用。

我们继续看:

class Person
{
public:
	void PersonInit()
	{
		cout << "void PersonInit()" << endl;
	}

//private:
	int _age;		//声明
	char _name[20];
};

int main()
{
	Person* p = nullptr; //初始化为空指针
	p->PersonInit();
	p->_age = 1;

	return 0;
}

这就会导致运行崩溃,对空指针的内容进行解引用。

我们接着上面看:

class Person
{
public:
	void PersonInit()
	{
		cout << _age << endl;
	}

//private:
	int _age;		//声明
	char _name[20];
};

int main()
{
	Person* p = nullptr; //初始化为空指针
	p->PersonInit();

	return 0;
}

这里在调用Init函数的时候,函数里面产生了解引用,但是this是空指针,这里就会运行崩溃。

空指针不会编译错误,只会导致运行崩溃。

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

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

相关文章

网络运维基础问题及解答

前言 本篇文章是对于网络运维基础技能的一些常见问题的解答&#xff0c;希望能够为进行期末复习或者对网络运维感兴趣的同学或专业人员提供一定的帮助。 问题及解答 1. 列举 3 种常用字符编码&#xff0c;简述怎样在 str 和 bytes 之间进行编码和解码。 答&#xff1a;常用的…

Python读取多个栅格文件并提取像元的各波段时间序列数据与变化值

本文介绍基于Python语言&#xff0c;读取文件夹下大量栅格遥感影像文件&#xff0c;并基于给定的一个像元&#xff0c;提取该像元对应的全部遥感影像文件中&#xff0c;指定多个波段的数值&#xff1b;修改其中不在给定范围内的异常值&#xff0c;并计算像元数值在每一景遥感影…

【C++】-动态内存管理

作者&#xff1a;小树苗渴望变成参天大树 作者宣言&#xff1a;认真写好每一篇博客 作者gitee:gitee 如 果 你 喜 欢 作 者 的 文 章 &#xff0c;就 给 作 者 点 点 关 注 吧&#xff01; 文章目录 前言一、C内存管理方式1.1 new/delete操作内置类型 总结 前言 今天再讲一个…

【禁用外键】为什么互联网大厂禁用外键约束?详谈外键的优缺点和使用场景

导航&#xff1a; 【Java笔记踩坑汇总】Java基础进阶JavaWebSSMSpringBoot瑞吉外卖SpringCloud黑马旅游谷粒商城学成在线MySQL高级篇设计模式常见面试题源码 目录 一、外键介绍 1.1 概述 1.2 练习 1.2.1 数据准备 1.2.2 验证有外键时&#xff0c;删除记录要维护外键 1.2…

Python批量下载主播照片,实现人脸识别, 进行颜值评分,制作颜值排行榜

昨晚一回家&#xff0c;表弟就神神秘秘的跟我说&#xff0c;发现一个高颜值网站&#xff0c;非要拉着我研究一下她们的颜值高低。 我心想&#xff0c;这还得要我一个个慢慢看&#xff0c;太麻烦了~ 于是反手用Python给他写了一个人脸识别代码&#xff0c;把她们的照片全部爬下…

06-行向量列向量_向量的运算 加法,数乘,减法,转置

行向量和列向量 行向量是按行把向量排开&#xff08;横着来写&#xff09;&#xff0c; 列向量是按列把向量排开&#xff08;竖着来写&#xff09; 在数学中我们更多的把数据写成列向量&#xff0c;在编程语言中更多的把数据存成行向量! 如果想在编程语言中把行向量转化成列…

人力资源管理系统servlet jsp人资企业办公java源代码mysql

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 人力资源管理系统ervlet jsp 系统有1权限&#xff1a…

【计算机网络】408统考2014年题36

题目描述 【2014年题36】主机甲与主机乙之间使用后退N帧(GBN)协议传输数据&#xff0c;甲的发送窗口尺寸为1000&#xff0c;数据帧长为1000字节&#xff0c;信道带宽为100Mbps&#xff0c;乙每收到一个数据帧就立即利用一个短帧&#xff08;忽略其传输延迟&#xff09;进行确认…

动态sql以及常用的标签

什么是动态sql&#xff1a; 指根据不同的条件生成不同的sql 搭建环境&#xff1a; 建表&#xff1a; create table blog( id varchar(50) not null comment 博客id, title varchar(100) not null comment 博客标题, author varchar(30) not null comment 博客作者, create_ti…

基于springboot+mybatis+mysql+vue+html民宿管理平台

基于springbootmybatismysqlvuehtml民宿管理平台 一、系统介绍二、功能展示1.首页2.个人中心3.民宿信息浏览(用户)4房间信息浏览&#xff08;用户&#xff09;5.房间预订&#xff08;用户&#xff09;6.房间退订&#xff08;用户&#xff09;7.投诉反馈&#xff08;用户&#x…

如何配置保存cpolar所建立的隧道参数?

文章目录 可以利用cpolar建立一个能发布到公共互联网的网页&#xff0c;这是基于我们直接对cpolar进行即时设置&#xff0c;获得了能在公共互联网上访问本地数据的二级子域名。但如果电脑关闭重启后&#xff0c;如何让电脑自动启动cpolar&#xff0c;并记住设置好的域名参数文件…

一文搞懂自动驾驶芯片TDA4 启动流程

文章目录 TDA4架构简介TDA4启动流程DMSC ROM阶段MCU域R5 ROM阶段SBL阶段 TDA4架构简介 TDA4是德州仪器推出的一款高性能、超异构的多核SoC&#xff0c;拥有ARM Cortex-R5F、ARM Cortex-A72、C66以及C71内核&#xff0c;可以部署AUTOSAR CP系统、HLOS(Linux或QNX)、图像处理以及…

tinkerCAD案例:23.Tinkercad 中的自定义字体

tinkerCAD案例&#xff1a;23.Tinkercad 中的自定义字体 原文 Tinkercad Projects Tinkercad has a fun shape in the Shape Generators section that allows you to upload your own font in SVG format and use it in your designs. I’ve used it for a variety of desi…

Python实现GA遗传算法优化循环神经网络分类模型(LSTM分类算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 遗传算法&#xff08;Genetic Algorithm&#xff0c;GA&#xff09;最早是由美国的 John holland于20世…

(文章复现)梯级水光互补系统最大化可消纳电量期望短期优化调度模型matlab代码

参考文献&#xff1a; [1]罗彬,陈永灿,刘昭伟等.梯级水光互补系统最大化可消纳电量期望短期优化调度模型[J].电力系统自动化,2023,47(10):66-75. 1.基本原理 1.1 目标函数 考虑光伏出力的不确定性&#xff0c;以梯级水光互补系统的可消纳电量期望最大为目标&#xff0c;函数…

递归求解汉诺塔问题(超详解)

问题提出 这个问题是关于三根柱子和一些圆盘的游戏。 初始时&#xff0c;所有的圆盘按照从大到小的顺序叠放在一根柱子上&#xff0c;目标是将所有圆盘从起始柱子移动到目标柱子上&#xff0c;在移动过程中&#xff0c;要满足以下规则喵&#xff1a; 每次只能移动一个圆盘。大圆…

混动才是未来?福特电车亏损数十亿美元,聚焦混动展望未来

福特汽车公司决定聚焦混合动力汽车&#xff0c;以弥补电动汽车市场亏损数十亿美元。吉姆法利首席执行官表示&#xff0c;在最新财报发布会上透露&#xff0c;未来将推出更多种类的混合动力车型。 福特最近公布了Q2盈亏情况&#xff0c;显示电动汽车部门的亏损有所增加。不过&am…

从源码角度配合网络编程函数accept() connect()等实现的客户端服务器通信 分析下 三握手四挥手都做了什么

首先我们先说下网络编程API&#xff1a; 数据在网络上通信&#xff0c;通信的双方一个是 客户端&#xff0c; 一个是 服务器 更具体来说&#xff0c;不是 客户端和服务器这两个机器在 经由互联网 进行通信&#xff0c; 而是 客户端上的某一进程 与 服务器端的某一进程 进…

vue3+ts未使用变量报错的解决

实例 问题原因 tsconfig.json文件中开启了ts语法检查 "strict": true, // 开启严格模式&#xff0c;检查类型声明和赋值...是否合法 "noUnusedLocals": true, // 检查是否存在未使用的变量 "noUnusedParameters": true, // 检查是否存在会使…

app性能测试怎么做?内容全在这里了

1 app性能测试 提到APP的性能测试这个概念比较笼统&#xff0c;因为APP的性能测试分为服务端的性能和手机端的性能测试 1.1 app服务端性能测试 app服务端的性能测试&#xff0c;利用jmeter等工具模拟并发&#xff0c;压测服务器系统&#xff0c;服务端性能测试&#xff0c;一…