类和对象、this指针、类里的默认生成函数

🐶博主主页:@ᰔᩚ. 一怀明月ꦿ 

❤️‍🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C++,linux

🔥座右铭:“不要等到什么都没有了,才下定决心去做”

🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀

目录

类和对象

类的引入

类的定义

类的定义方式

1.声明和定义放在类体

2. 声明放在.h文件中,类的定义放在.cpp文件中

类的访问限定符及封装

访问限定符

类的作用域 

类的实例化

类对象模型

结构体内存对齐规则

this指针

this指针的特性

this指针存在哪里?

this指针可以为空吗?

补充

类里的默认生成函数

构造函数

析构函数


类和对象

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

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

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

类的引入

C语言中,结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。

struct ADD
{void add(int x,int y){cout<<x+y<<endl;}int a;int b;
};

C++兼容C语言,结构体可以继续使用,同时struct也升级成了类

struct A
{int age;int score;
};
int main()
{struct A a1;A a2;
}
定义变量时,既可以struct A a1;,也可以A a2;

但是在C++中更喜欢使用class来代替

类的定义

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

class为定义类的关键字,类中的元素称为类的成员,类中的数据称为类的属性或者成员变量; 类中的函数称为类的方法或者成员函数。

类的定义方式

1.声明和定义放在类体

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

class student
{void show(){cout<<_name<<_sex<<_age<<endl;}char* _name;char* _sex;int _age;
};
2. 声明放在.h文件中,类的定义放在.cpp文件中
class student
{void show();char* _name;char* _sex;int _age;
};#include"main.h"
void student::show()
{cout<<_name<<_sex<<_age<<endl;
}
建议我们使用第二种方法

类的访问限定符及封装

访问限定符

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

1. public修饰的成员在类外可以直接被访问

2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)

3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止

4. class的默认访问权限为private,struct为public(因为struct要兼容C)

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

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

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

封装

封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。

类的作用域 

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

例如成员函数的类外定义

void student::show()
{cout<<_name<<_sex<<_age<<endl;
}

类的实例化

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

1. 类只是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它

2. 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量

3. 做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间

class student
{void show();char* _name;char* _sex;int _age;
};
int main()
{student s1;return 0;
}
其中的student类实例化了一个对象s1

类对象模型

如何计算类对象的大小

首先我们得搞明白类的存储方式:只保存成员变量,成员函数放在公共的代码代码段

//成员函数和成员变量
class student1
{void show(){}char* _name;char* _sex;int _age;
};
//成员函数
class student2
{void show(){}
};
//成员变量
class student3
{char* _name;char* _sex;int _age;
};
//空类
class student4
{
};
int main()
{cout<<"class student1:"<<sizeof(student1)<<endl;cout<<"class student2:"<<sizeof(student2)<<endl;cout<<"class student3:"<<sizeof(student3)<<endl;cout<<"class student4:"<<sizeof(student4)<<endl;
}
结果:
class student1:24
class student2:1
class student3:24
class student4:1

结论:一个类的大小,实际就是该类中”成员变量”之和,当然也要进行内存对齐,注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类(就是告诉编译器这个类存在)。

结构体内存对齐规则

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

对齐数的详细分析:「自定义类型」C语言中的构造数据类型如结构,联合,枚举-CSDN博客

this指针

在C++中,this指针是一个特殊的指针,它指向当前对象的地址。它是作为类的非静态成员函数的隐式参数传递给函数的,可以在成员函数中使用它来访问当前对象的成员变量和成员函数。

事例

class Box
{
public:Box(double length,double width,double height){_length=length;_width=width;_height=height;}void volume(){cout<<_length*_width*_height<<endl;}
private:double _length;double _width;double _height;
};
int main()
{Box box1(2,3,5);Box box2(1,2,3);
}

c++为类的对象分配内存空间时只为对象的数据成员分配内存空间,而将对象的成员函数放在另外一个公共区域,同一个类的多个对象共享它们的成员函数。那么,同一个类的多个对象的成员函数在访问对象的数据成员时,怎么确保访问的是正确的数据成员呢?例如声明的长方体类Box,定义了两个对象box1和box2,对于调用“box1.volume()”,应该box1中的_height,_width和_length计算长方体box1的体积,对于“box2.volume()”,应该box2中的_height,_width和_length计算长方体box1的体积。现在box1和box2调用的都是同一段代码,系统是怎么区分应该访问box1的数据成员还是box2的数据成员呢?


其实在每一个成员函数中都包含了一个特殊的指针,这个指针的名字是固定的,称为this指针。this指针是指向本类对象的指针,它的指向是被调用成员函数所在的对象,即调用哪个对象的该成员函数,this指针就指向哪个对象。在成员函数内部访问数据成员的前面隐藏着this指针。如前面前面提到的Box类中的volume函数,其中的height*width*length实际等价于(this->_height)*(this->_width)*(this->_length)。如果调用box1对象的volume函数,则this指针就指向对象box1,所以(this->_height)*(this->_width)*(this->_length)就相当于(box1._height)*(box1._width)*(box1._length),这样就是计算的box1的体积。

this指针的特性

1. this指针的类型:类类型* const(指针常量)

2. 只能在“成员函数”的内部使用

3. this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。

4. this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递

this指针存在哪里?

其实编译器在生成程序时加入了获取对象首地址的相关代码。并把获取的首地址存放在了寄存器ECX中(vs2019编译器是放在ECX中,其它编译器有可能不同)。也就是成员函数的其它参数正常都是存放在栈中。而this指针参数则是存放在寄存器中。类的静态成员函数因为没有this指针这个参数,所以类的静态成员函数也就无法调用类的非静态成员变量。

this指针可以为空吗?

可以为空,当我们在调用函数的时候,如果函数内部并不需要使用到this,也就是不需要通过this指向当前对象并对其进行操作时才可以为空(当我们在其中什么都不放或者在里面随便打印一个字符串),如果调用的函数需要指向当前对象,并进行操作,则会发生错误(空指针解引用)

补充

下面的运行结果?1.编译报错    2.运行崩溃    3.正常运行

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

结果是正常运行,调用函数分为两步,第一步传参,第二步传函数的地址,成员函数的地址不存在对象里面,成员函数地址在公共代码区域,所以 p->Print();没有对p解引用,所以不会报错。

下面的运行结果?1.编译报错    2.运行崩溃    3.正常运行

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

结果是运行崩溃,cout<<_a<<endl;这里会转化为 cout<<this->_a<<endl;this是指向p,所以这里对空指针解引用,所以运行崩溃

类里的默认生成函数

如果一个类中什么成员都没有,简称为空类。空类中什么都没有吗?并不是的,任何一个类在我们不写的情况下,都会自动生成下面6个默认成员函数

1.构造函数

2.析构函数

3.拷贝构造函数

4.赋值运算符的重载函数

5.取地址操作符的重载函数

6.const修饰的取地址操作符的重载函数

构造函数

构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有 一个合适的初始值,并且在对象的生命周期内只调用一次

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

其特征如下:

1. 函数名与类名相同。

2. 无返回值。

3. 对象实例化时编译器自动调用对应的构造函数。

4. 构造函数可以重载

class Date
{
public://无参数的构造函数Date(){}//有参数的构造函数Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Display(){cout <<_year<< "-" <<_month << "-"<< _day <<endl;}
private:int _year;int _month;int _day;};
int main()
{//注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明Date d1;//调用无参的构造函数Date d2(2023,7,20);//调用带参数的构造函数return 0;
}

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

class Date
{
public:void Display(){cout <<_year<< "-" <<_month << "-"<< _day <<endl;}
private:int _year;int _month;int _day;};
int main()
{Date d;return 0;
}

6.无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认成员函数。

class Date
{
public://无参数的构造函数Date(){}//全缺省参数的构造函数Date(int year=0, int month=0, int day=0){_year = year;_month = month;_day = day;}void Display(){cout <<_year<< "-" <<_month << "-"<< _day <<endl;}
private:int _year;int _month;int _day;};
int main()
{Date d1;Date d2(2023,7,20);return 0;
}

这里编译会出错, Date d1;创建d1这个对象时,没有传递参数,但是构造函数一个是无参的,一个是全缺省的,编译器不知道调用哪一个,这里就是典型的二义性错误。

7.关于编译器生成的默认成员函数,很多人就会疑惑:在我们不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d对象_year,_month,_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有用?

C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语法已经定义好的类型:如int/char…(所有的指针类型都是内置类型),自定义类型就是我们使用class/struct/union自己定义的类型,看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数

class Time
{
public:Time(int hour=0, int minute=0, int second=0){cout<<"Time(int hour=0, int minute=0, int second=0)"<<endl;_hour=hour;_minute=minute;_second=second;}
private:int _hour;int _minute;int _second;
};
class Date
{
private://基本类型(内置类型)int _year;int _month;int _day;//自定义类型Time _time1;
};
int main()
{Date d;return 0;
}
结果:
Time(int hour=0, int minute=0, int second=0)

析构函数

概念

前面通过构造函数的学习,我们知道一个对象时怎么来的,那一个对象又是怎么没呢的?

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

特性

析构函数是特殊的成员函数。

其特征如下: 

1. 析构函数名是在类名前加上字符 ~。

2. 无参数无返回值。

3. 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。

4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

class Date
{
public:Date(int year=0, int month=0, int day=0){_year = year;_month = month;_day = day;}~Date(){cout<<"~Date()"<<endl;}
private:int _year;int _month;int _day;};
int main()
{Date d;return 0;
}
结果:
~Date()

5. 关于编译器自动生成的析构函数,是否会完成一些事情呢?下面的程序我们会看到,编译器生成的默认析构函数,对会自定类型成员调用它的析构函数。

class Time
{
public:Time(int hour=0, int minute=0, int second=0){cout<<"Time(int hour=0, int minute=0, int second=0)"<<endl;_hour=hour;_minute=minute;_second=second;}~Time(){cout<<"~Time()"<<endl;}
private:int _hour;int _minute;int _second;
};
class Date
{
private://基本类型(内置类型)int _year;int _month;int _day;//自定义类型Time _time1;
};
int main()
{Date d;return 0;
}
结果:
Time(int hour=0, int minute=0, int second=0)
~Time()

设计析构函数的本意是对象生命周前结束前完成资源清理,比如,在C语言中写的栈,有个函数是完成栈的销毁,但是我们很容易忘记调用,在这里我们就可以把销毁的函数放在析构函数中,系统会自动调用。资源清理主要是清理系统不能执行的,清理动态内存申请的空间等。其实我们还可以利用析构函数会被自动调用的特性,完成一些打印的功能等。

  🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何恢复回收站中被删除的文件?3个恢复策略,实测有用!

“刚刚一不小心把回收站清空了&#xff0c;大家有什么好用的方法可以帮我恢复回收站中删除的文件吗&#xff1f;快帮帮我吧&#xff01;” 在使用电脑的过程中&#xff0c;我们有时可能会不小心将重要的文件或文件夹删除到回收站&#xff0c;并且随后可能进一步从回收站中彻底删…

【Git】Git学习-17:git rebase,且解决合并冲突

学习视频链接&#xff1a;【GeekHour】一小时Git教程_哔哩哔哩_bilibili​编辑https://www.bilibili.com/video/BV1HM411377j/?vd_source95dda35ac10d1ae6785cc7006f365780 理论 git rebase 目标分支&#xff1a;把当前分支的提交&#xff0c;从与目标分支的共同主祖先处断开…

【linux】dmesg工具

dmesg介绍 dmesg工具用途&#xff1a; dmesg - print or control the kernel ring buffer kernel ring buffer, 内核环形缓冲区&#xff0c;也叫环形队列&#xff0c;Linux内核日志就存储在一个环形队列中&#xff0c;环形队列满的时候&#xff0c;新的消息会覆盖掉旧的消息。…

UE5自动生成地形一:地形制作

UE5自动生成地形一&#xff1a;地形制作 常规地形制作地形编辑器地形管理添加植被手动修改部分地形的植被 置换贴图全局一致纹理制作地貌裸露岩石地形实例 常规地形制作 地形制作入门 地形导入部分 选择模式&#xff1a;地形模式。选择地形子菜单&#xff1a;管理->导入 …

Windows电脑搭建HarmonyOS NEXTDeveloper Preview2环境详解

Windows电脑搭建HarmonyOS NEXTDeveloper Preview2环境详解&#xff1a; HarmonyOS NEXT Preview系列教程基于Api11讲解-IT营大地老师 1 、电脑要求以及注意事项 操作系统 &#xff1a; Windows10 64 位、 Windows11 64 位 内存 &#xff1a; 8GB 及以上&#xff0c;推荐 16G…

“情况不明,对子先行”攻略

掼蛋作为一种策略性极强的游戏&#xff0c;不仅考验牌技&#xff0c;更考验玩家的智慧和策略布局。这里主要介绍一下当牌力不足的时候的普通策略—情况不明&#xff0c;对子先行。 当你的牌力不强&#xff0c;或者牌局情况不明朗时&#xff0c;自己手上有有比较多的对子&#x…

【Ansible】ansible-playbook剧本

playbook 是ansible的脚本 playbook的组成 1&#xff09;Tasks&#xff1a;任务&#xff1b;通过tasks 调用ansible 的模板将多个操作组织在一个playbook中运行 2&#xff09;Variables&#xff1a;变量 3&#xff09;Templates&#xff1a;模板 4&#xff09;Handles&#xf…

如何开发自己的深度学习优化算法

深度学习优化算法 如何开发自己的深度学习优化算法理解优化算法的基础**核心组件**&#xff1a; 设计自定义优化算法的步骤**步骤 1: 定义问题和目标****步骤 2: 研究现有算法****步骤 3: 开发初步想法****步骤 4: 创建原型****步骤 5: 系统测试与优化** 关键建议 如何开发自己…

C 语言指针怎么理解?

在今天的学习中&#xff0c;我注意到有位学员似乎对 C 语言指针的理解有些困惑。为了帮助大家更好地理解&#xff0c;我来举个例子。 C 语言指针就好比 Windows 桌面上常见的快捷方式。快捷方式可以指向某个游戏&#xff0c;这就是普通指针&#xff1b;它也可以指向另一个快捷…

Apache DolphinScheduler 3.3.0 版本重磅更新提前看!

Apache DolphinScheduler 3.3.0版本终于要在万众期待中发布啦&#xff01;本次发版将有重大功能更新&#xff0c;包括架构上的调整。 为了让广大用户提前尝鲜&#xff0c;社区特别准备了直播活动提前揭秘3.3.0版本中的重要更新&#xff0c;到时候你将会了解到这些信息&#xf…

(四)小程序学习笔记——自定义组件

1、组件注册——usingComponents &#xff08;1&#xff09;全局注册&#xff1a;在app.json文件中配置 usingComponents进行注册&#xff0c;注册后可以在任意页面使用。 &#xff08;2&#xff09;局部注册&#xff0c;在页面的json文件中配置suingComponents进行注册&#…

vector、heap数组、stack数组访问性能验证

测试目的 本次测试旨在比较不同数据结构&#xff08;vector、数组&#xff09;以及不同访问方法&#xff08;[]、at()、offset&#xff09;在性能上的差异&#xff0c;从而为开发者提供在特定情境下做出最佳选择的依据。 测试代码 测试网址:Quick C Benchmarks 使用GCC9.5 …

pxe远程安装

PXE 规模化&#xff1a;可以同时装配多台服务器 自动化&#xff1a;自动安装操作系统和各种配置 不需要光盘U盘 前置需要一台PXE服务器 pxe是预启动执行环境&#xff0c;再操作系统之前运行 实验&#xff1a; 首先先关闭防火墙等操作 [rootlocalhost ~]# systemc…

C++成员初始化列表

我们在类的构造函数中使用成员初始化列表可以带来效率上的提升&#xff0c;那么成员初始化列表在编译后会发生什么就是这篇文章要探究的问题 文章目录 引入成员初始化列表用成员初始化列表优化上面的代码成员初始化列表展开成员初始化列表的潜在危险 参考资料 引入 考虑下面这…

电脑提示mfc140u.dll文件丢失了?怎么快速修复mfc140u.dll文件

当你的电脑提示你的mfc140u.dll文件丢失了&#xff0c;那么就要小心了&#xff0c;可能你的某些程序出问题了&#xff01;这时候需要我们去进行相关的修复&#xff0c;只有修复了这个mfc140u.dll文件&#xff0c;才能正常的使用某些程序。下面一起来了解一下mfc140u.dll文件吧。…

【JavaEE精炼宝库】计算机是如何工作的

目录 前言&#xff1a; 一、冯诺依曼体系 二、CPU基本知识 2.1 硬盘|内存|CPU关系&#xff1a; 2.2 指令&#xff1a; 2.3 CPU是如何执行指令的&#xff08;重点&#xff09;&#xff1a; 2.4 小结&#xff1a; 三、编程语言 3.1 程序&#xff1a; 3.2 编程语言发展&a…

区块链 | NFT 水印:Review on Watermarking Techniques(二)

&#x1f34d;原文&#xff1a;Review on Watermarking Techniques Aiming Authentication of Digital Image Artistic Works Minted as NFTs into Blockchains 1 半脆弱和可逆水印 鲁棒性好的水印技术通常会产生非常低透明度。正如前面所述&#xff0c;由于透明度在处理数字…

冯喜运:5.8黄金原油今日行情走势及最新操作建议

【黄金消息面分析】&#xff1a;金价周三&#xff08;5月8日&#xff09;亚市小幅走弱&#xff0c;现货黄金一度下跌0.3%至2306.94美元/盎司附近&#xff0c;市场参与者在等待美联储官员提供新的线索&#xff0c;以进一步明确潜在的降息时间表&#xff0c;同样在黄金日线图中&a…

vue3专栏项目 -- 项目介绍以及准备工作

这是vue3TS的项目&#xff0c;是一个类似知乎的网站&#xff0c;可以展示专栏和文章的详情&#xff0c;可以登录、注册用户&#xff0c;可以创建、删除、修改文章&#xff0c;可以上传图片等等。 这个项目全部采用Composition API 编写&#xff0c;并且使用了TypeScript&#…

使用Docker安装MySQL5.7.36

拉取镜像并查看 docker pull mysql:5.7.36拉取成功后查看&#xff08;非必须&#xff09; docker images创建并设置宿主机 mysql 配置文件目录和数据文件目录 创建相关文件夹将容器中的mysql数据保存到本地&#xff0c;这样即使容器被删除&#xff0c;数据也不会丢失。 mkd…