C++第五节 - this指针、构造函数、析构函数

news2024/11/12 21:53:27

一、类对象的存储方式

只保存成员变量,成员函数存放在公共的代码段

 注意点:

#include<iostream>
using namespace std;
class A
{
public:
	void PrintA()
	{
		cout << _a << endl;
	}
private:
	char _a;
};
int main()
{
	class A a;
	cout << sizeof(a) << endl;
	cout << sizeof(A) << endl;
	return 0;
}

sizeof求大小的时候输入对象名和类型名的效果是一样的!

int main()
{
	class A a;
	//cout << sizeof(a) << endl;
	//cout << sizeof(A) << endl;
	a._a = 1;
	a.PrintA();
	return 0;
}

a._a是在对象里面找空间,将其值覆为1;

a.PrintfA()不是在对象中寻找!(函数存放在公共代码段中)

剩下的成员变量是按照C语言中的结构体的内存对齐来存储的!

下面为两个class内存对齐的例子:

  • 第一个class,存放char的时候,对其数为1,因此挨着int存放;
  • 第二个class,存放int的时候,其对其数为4,因此要在对其数的整数倍处存放;
  • 整个结构体的大小为对其数的整数倍;

为什么要进行内存对齐?

对于不同的硬件来说,访问内存并不是说想访问那个字节就访问哪个字节,具体的访问数量跟硬件的设置有关系;

假设硬件CPU一次读取四个字节

  • 访问_ch的时候访问到了4个字节;但是此时可以直接抛弃下面的3个字节,仅仅实现读取一次就完成!
  • 访问_a的时候直接访问4个字节一次读取即可完成;

如果没有内存对齐:

此时读取a需要读取两次!每次只能读取一部分!

怎么修改对其数呢?

#include <stdio.h>
#pragma pack(1)//设置默认对⻬数为1
struct S
{
 char c1;
 int i;
 char c2;
};
#pragma pack()//取消设置的对⻬数,还原为默认

调整对其数后,会造成访问效率下降,但是能节省空间;

注意点:空类和仅有成员函数

class A2 {
public:
	void f2() {}
};
// 类中什么都没有---空类
class A3
{};

int main()
{
	class A2 a2;
	class A3 a3;
	cout << sizeof(a2) << endl;
	cout << sizeof(a3) << endl;
	return 0;
}

没有成员变量的类对象,需要1byte,是为了占位,表示对象存在!这个字节不存储有效数据!

不给1个byte的内存,此时如果对对象取地址,无法取地址!

二、this指针

Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函
数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?

其实,方框类似于编译器自己做的处理,会将对象的地址传过去,然后通过地址调用不同对象所对应的成员函数;(自己不能显示的去写)

  • this不能在形参和实参显示传递,但是可以在函数内部显示使用!
  • this的实际类型为Date* const this,const修饰的是this指针,因此this指针不能被修改,但是this指向的内容可以被修改!(例如this = nullptr是错误的!)

问题1:this指针存放在哪里?(对象里面,堆,栈,静态区,常量区)

this是形参,因此this指针跟普通的参数一样存放在函数调用的栈帧里面!(函数调用结束后,this指针销毁!)

如果this指针存放在对象里面,那么空类得大小就不会为1!

lea指令的作用是将取d1的地址放到ecx中! 

问题2:

空指针访问是运行的问题,而不是编译的错误!(因此排除A);

  • p调用Print,不会发生解引用,因为Printf的地址不在对象中,p会作为实参传递给this指针;
  • this是空的,但是函数内部没有堆this进行解引用操作,因此不会发生报错!

调用Print()函数是直接调用,没有发生解引用! 

正确答案为: C

问题3:

  • p调用Print,不会发生解引用,因为Printf的地址不在对象中,p会作为实参传递给this指针;
  • this是空的,但是函数内部访问了_a,this->_a,本质上是通过指针进行解引用(汇编指令是解引用)操作找到_a,因此会报错!

正确答案为: B 

访问类的成员函数不能使用::限定符,因为使用.传递的时候,编译器会自动传入this指针,但是使用::的时候不知道需要传入什么值!

如果自己传递的话就不满足this指针不能显示传递的条件!

  1. 点运算符(.

    • 点运算符用于访问对象的实例成员。它表示你在访问某个特定对象的属性或方法。例如,如果你有一个类 Data 的实例 data,你可以通过 data.a 来访问 a 这个成员变量。
  2. 双冒号运算符(::

    • 双冒号运算符通常用于访问类的静态成员或命名空间中的成员。在一些语言(如 C++ 和 PHP)中,ClassName::member 用于访问类的静态属性或方法,而不是实例的属性。

因此,Data::a 通常表示你在尝试访问 Data 类的静态成员 a,而 Data.a 则表示你在访问 Data 类的实例 data 的成员变量 a。如果 a 是一个实例变量,你必须先创建 Data 类的一个实例,然后通过该实例来访问 a

总结:this指针的特性

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

三、使用C和C++实现栈

        C++实现栈本质上和C没有大的区别,但是C++都被封装到一起,使用起来更方便!不需要自己传入指针参数!

        即C++中 Stack * 参数是编译器维护的,C语言中需用用户自己维护。

  • 当我们自己在写代码的时候,经常会忘记Init或者Destory,如果不进行Destory就会造成内存泄露!
  • 有的地方写起来很繁琐!例如下面的,我们还不能直接对Stack进行销毁,还需要先保存值,然后将stack销毁,再返回ret;

引入:构造函数主要完成初始化工作;(不是创建对象,对象已经在函数栈帧自动创建了!会自动调用函数,函数的功能不是创建对象,而是做初始化工作!)

           析构函数主要完成清理工作;(不是创建对象!会自动调用函数,函数的功能不是销毁对象,而是做清理工作!)

四、构造函数

        构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象。

其特征如下:

  1. 函数名与类名相同。
  2. 无返回值。(也不需要写Void)
  3. 对象实例化时编译器自动调用对应的构造函数。
  4. 构造函数可以重载。

如果把构造函数设置为private,语法上没有问题,但是直接运行会报错!

当我们不写构造函数的时候,编译器会默认生成构造函数,内置类型不做处理,自定义类型会去调用他的默认构造!

例如VS2013

此时会发现自定义类型_st被初始化,但是内置类型没有被初始化!

但是VS2019会自动对两种变量都进行处理:

结论:自定义类型一定会被初始化,但是内置类型可能不会被初始化,具体结果取决于编译器!

且如果没有自定义类型,内置类型不会被初始化!

  1. 一般情况下,如果有内置类型,就需要自己写构造函数!
  2. 全部都是自定义类型的成员,可以考虑让编译器自己生成!

C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在
类中声明时可以给默认值。(这里不是初始化,这里只是声明,这里给的是默认的缺省值,给编译器生成默认构造函数用)

没有给默认值:

class Time
{
public:
 Time()
 {
 cout << "Time()" << endl;
 _hour = 0;
 _minute = 0;
_second = 0;
 }
private:
 int _hour;
 int _minute;
 int _second;
};

给定默认值: 

class Date
{
private:
 // 基本类型(内置类型)
 int _year = 1970;
 int _month = 1;
 int _day = 1;
 // 自定义类型
 Time _t;
};
int main()
{
 Date d;
 Date d(2020,2,2);  // 修改默认值
 return 0;
}

构造函数的调用

普通函数是对象+成员函数;

构造函数是类+对象(参数列表);

且构造函数的调用不能采用以下方式:

Data d1();

没有参数的时候不能这样调用!

这样子写的话会跟函数声明有冲突,编译器不好识别!

可能会把d1当作函数名,返回类型为Data!

注意点:无参和全缺省函数构成函数重载,但是在调用的时候存在歧义!因此也不能同时存在!

        默认构造函数:无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
        注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为
是默认构造函数。(即不传参就可以调用的就是默认构造函数!)

        这三个构造函数有且只能存在1个!

  •         class类中默认成员函数有6个!即我们不写但是编译器会自己生成!
  •         但是默认构造函数不一定是编译器自己写的!

五、析构函数

析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由
编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

析构函数是特殊的成员函数,其特征如下:

  1. 析构函数名是在类名前加上字符 ~。
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载。(默认生成的对内置类型不做修改,对自定义类型做处理)
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

5. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦
用户显式定义编译器将不再生成。

  • 内置类型/基本类型,语言本身定义的基本类型int/char/double/指针等;
  • 自定义、用struct/class等等定义的类型;

 

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

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

相关文章

CSS“多列布局”(补充)——WEB开发系列35

多列布局是一种非常常见的布局方式&#xff0c;适用于内容丰富的页面&#xff0c;如新闻网站、杂志或博客。 一、CSS多列布局概述 CSS多列布局允许我们将内容分成多个垂直列&#xff0c;使页面布局更加灵活和多样化。多列布局的主要属性包括 ​​column-count​​、​​column…

django学习入门系列之第十点《A 案例: 员工管理系统4》

文章目录 6 部门管理&#xff08;原始方式&#xff09;6.6 添加界面的导入&#xff08;数据库&#xff09;6.7 删除按键的应用6.8 编辑按键的应用6.81 传值的另一种方式 6.9 提交按键的应用 往期回顾 6 部门管理&#xff08;原始方式&#xff09; 6.6 添加界面的导入&#xff…

E32.【C语言 】练习:蓝桥杯题 懒羊羊字符串

1.题目 【问题描述】 “懒羊羊”字符串是一种特定类型的字符串&#xff0c;它由三个字符组成&#xff0c;具有以下特点: 1.字符串长度为 3. 2.包含两种不同的字母。 3.第二个字符和第三个字符相同 换句话说&#xff0c;“懒羊羊”字符串的形式应为 ABB&#xff0c;其中A和B是不…

【python2C】算法基础:计时比较

不断改进代码&#xff0c;是学习进步必经之路。 判断代码优劣&#xff0c;在空间允许的情况下&#xff0c;计时就是最可靠的标尺。 打表不算&#xff0c;人脑不算 1.对于答案较为固定的题&#xff0c;预先算出所有可能的答案表&#xff0c;然后对应输入查找答案&#xff0c;从…

【OpenGL 002】着色器 GLSL 语言及GLFW代码案例

文章目录 1.GLSL语言简介本节案例 code 1 2.GLSL的数据类型① 向量(Vector)- 向量重组示例- 向量重组禁忌 3.GLSL的输入输出本节案例 code 2 4.着色器示例5.Uniform本节案例 code 3 通过第一节 【OpenGL 001】Ubuntu 搭建 GLFW 环境及其相关测试 demo 想必已经搭建好了GLFW环境…

『功能项目』GameObject对象池 - 第三职业【39】

本章项目成果展示 我们打开上一篇38管理器基类的项目&#xff0c; 本章要做的事情是利用对象池制作第三个职业——魔法师 在GameRoot对象下创建空物体 重命名为PoolRoot 将GameRoot拖拽至预制体 创建脚本&#xff1a; 编写脚本&#xff1a;PoolManager.cs using UnityEngine;…

使用豆包MarsCode 编写 Node.js 全栈应用开发实践

以下是「豆包MarsCode 体验官」优秀文章&#xff0c;作者狼叔。 欢迎更多用户使用豆包MarsCode 并分享您的产品使用心得及反馈、创意项目开发等&#xff0c;【有奖征集&#xff5c;人人都是豆包MarsCode 测评官&#xff01;】活动正在火热进行中&#xff0c;欢迎大家投稿参加&a…

【Python报错已解决】 SyntaxError: invalid syntax

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 前言一、问题描述1.1 报错示例1.2 报错分析1.3 解决思路 二、解决方法2.1 方法一&#xff1a;修复缺失的括号或引号2.…

甘肃五仁月饼:传统风味,舌尖上的乡愁

在中秋佳节的美食画卷中&#xff0c;甘肃食家巷五仁月饼以其独特的魅力占据着重要的一席之地。甘肃五仁月饼&#xff0c;那朴实无华的外表下&#xff0c;藏着无尽的美味与情怀。它的饼皮金黄酥脆&#xff0c;散发着淡淡的麦香&#xff0c;仿佛在诉说着古老的制作工艺。轻轻咬上…

Java:动态代理

Java&#xff1a;动态代理 什么是代理 代理模式 是一种设计模式&#xff0c;它为其他对象提供了一种代理以控制对这个对象的访问。代理对象通常包装实际的目标对象&#xff0c;以提供一些附加的功能&#xff08;如延迟加载、访问控制、日志记录等&#xff09;。我们一般可以使…

C++中的内存管理和模板初识

一、内存管理 1.1内存区域的划分 1.1.1内存划分区域图示 1.1.1补&#xff1a;堆和栈都可以进行动态分配和静态分配吗&#xff1f; 不是的&#xff0c;堆无法进行静态分配&#xff0c;只能动态分配&#xff1b;栈可以利用_alloca动态分配&#xff0c;但是分配的空间不能用fre…

基于Logistic-Map混沌序列的数字信息加解密算法matlab仿真,支持对文字,灰度图,彩色图,语音进行加解密

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于Logistic-Map混沌序列的数字信息加解密算法matlab仿真,系统包含GUI操作界面&#xff0c;系统支持对文字,灰度图,彩色图,语音进行加解密。 2.测试软件版本以及…

项目运行插件-日志管理

日志管理 项目运行时模块提供了项目日志收集&#xff0c;检索和保存查询方案等功能。 体验地址&#xff1a; http://119.163.197.219:13456/view/runtime/index.html#/log/aioLogPage 沟通加QQ群 &#xff1a; 908377977 gitee 开源地址 &#xff1a; https://gitee.com/aio…

打印文档时,只有图片中的文本不清晰该如何处理

最近打印东西的时候&#xff0c;发现只有图片中的文本并不清晰&#xff0c;就想研究一下如何改善这个问题。 打印机是佳能的 MF113w&#xff0c;一个不错的多功能激光黑白打印机&#xff0c;支持无线打印。唯一问题就是每次 DHCP 分配 IP 到期后&#xff0c;这款打印机就会亮错…

AI提质增效率赋能工业产品质检,基于高精度YOLOv5全系列参数【n/s/m/l/x】模型开发构建工业生产场景下PCB电路板缺陷问题智能化分割检测识别分析系统

在PCB电路板的生产制造过程中&#xff0c;质量检测是确保产品质量、维护品牌形象的关键环节。然而&#xff0c;传统的人工检测方式依赖于经验丰富的工人师傅通过光学显微镜等设备进行逐块检查&#xff0c;这不仅劳动强度大、效率低下&#xff0c;而且受限于人的主观判断、视力疲…

《华为 eNSP 模拟器安装教程》

1.电脑安装环境要求&#xff1a; 检查电脑是否安装过 eNSP 和依赖软件&#xff0c;如果有&#xff0c;请全部卸载。 安装软件列表&#xff1a; 2.软件安装&#xff1a; 安装 WinPcap&#xff1a; 打开安装包&#xff0c;单击【Next】 单击【I Agree】 单击【Install】 单击【…

《信息系统安全》课程实验指导

第1关&#xff1a;实验一&#xff1a;古典密码算法---代换技术 任务描述 本关任务&#xff1a;了解古典密码体制技术中的代换技术&#xff0c;并编程实现代换密码的加解密功能。 注意所有明文字符为26个小写字母&#xff0c;也就是说字母表为26个小写字母。 相关知识 为了完…

1、常用的数据库、表操作

基本的建表和数据库拷贝操作。 一、数据定义语言DDL show databases; # 查看全部数据库 show create database db; # 查看数据库db create database db; # 创建数据库db drop database db; # 删除数据库db use db; # 使用数据库db基本…

1 Linux SSH安全加固_linux system-auth

![在这里插入图片描述](https://img-blog.csdnimg.cn/20201117150524918.png?x-oss-processimage/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwOTA3OTc3,size_16,color_FFFFFF,t_70#pic_center) ![在这里插入图片描述](https://…

11、LLaMA-Factory自定义数据集微调

1、数据集定义 针对实际的微调需求&#xff0c;使用专门针对业务垂直领域的私有数据进行大模型微调才是我们需要做的。因此&#xff0c;我们需要探讨如何在LLaMA-Factory项目及上述创建的微调流程中引入自定义数据集进行微调。**对于LLaMA-Factory项目&#xff0c;目前仅支持两…