C++ - 面向对象(二)

一. 类的6个默认成员函数 

在我们前面学习的中,我们会定义成员变量成员函数,这些我们自己定义的函数都是普通的成员函数,但是如若我们定义的类里什么也没有呢?是真的里面啥也没吗?如下

class Date {};

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

注意:这里的“默认”和“缺省”的意思差不多,也就是你不写这6个函数,编译器会自动生成,你若是写了,则编译器就不生成了。

1.1 为什么会出现默认成员函数

我们在日常写代码和刷题的时候,肯定会有过忘记初始化,或者忘记销毁造成了内存的泄漏,这些细节很容易被大家忽略,而C++祖师爷

忘记写初始化:输出随机值,结果会错误,且编译不通过

忘记写销毁:时间久了便会造成内存泄漏,如果是一次大型的泄露还容易被察觉,但是小型的泄露可能会导致后期出现大问题

二 .构造函数

2.1 构造函数的概念

如下的日期类:

class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << "今日日期输出:" << endl;
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	d1.Init(2024,4,14);
	d1.Print();
	return 0;
}
从运行结果上看,没初始化直接访问输出的是随机值。 忘记初始化其实是一件很正常的事情,C++大佬在这一方面为了填补C语言的坑(必须得手动初始化)。因而就设计出了 构造函数。
构造函数:特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任
并不是开空间创建对象,而是初始化对象

2.2 构造函数的特性

一. 函数名与类名相同

二. 无返回值,也不需要写void

如下即为构造函数: 

class Date
{
    Date(int year = 0, int month = 1, int day = 1)// 全缺省的构造函数
    {
	    _year = year;
	    _month = month;
	    _day = day;
    }
}

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

当我们在实例化一个对象后,它会自动调用这个构造函数,就自动完成了初始化 

四. 构造函数可以重载

这意味着你可以有多种初始化对象的方式,编译器会根据你所传递的参数去调用对应的构造函数。

class Date
{
    Date()
    {
	    _year = 1;
	    _month = 0;
	    _day = 0;
    }

    Date(int year, int month, int day)  //构造函数的重载
    {
	    _year = year;
	    _month = month;
	    _day = day;
    }
}

int main()
{
    Date s1;
    Date s2(2024, 4, 14); //参数写在实例化对象后面进行传参
    return 0;
}

注意: 

  • 没有参数时在调用的时候不能加上括号(),切忌!!构造函数尤为特殊
  • 如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明 

  • 构造函数的重载我们推荐写成全缺省的样子:
//普通的构造函数
	Date()
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}
//全缺省的构造函数
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

普通的构造函数和全缺省的构造函数在不调用的情况下可以同时存在,编译也没有错误。但是在实际调用的过程中,会存在歧义。如下的调用: 

 由此可见:它们俩在语法上可以同时存在,但是使用上不能同时存在,因为会存在调用的歧义,不知道调用的是谁,所以一般情况下,更推荐直接写个全缺省版的构造函数,因为是否传参数可由你决定。传参数数量也是由你决定。

五. 如果类中没有显示定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,若用户显示定义了,则编译器就不再生成

但是不是说好我不自己写构造函数,编译器会默认生成吗?为什么到这又是随机值了?这个随机值算是初始化吗?我们先来理解一下什么是默认构造函数

默认构造函数:

  • 我们不写,编译器自动生成的构造函数。
  • 无参构造函数也可以叫默认构造函数
  • 全缺省的也可以叫默认构造函数

总结:可以不传参数就调用构造,都可以叫默认构造

所以上述的随机值算是初始化,也是自动的调用了第一种类型(编译器自动生成的构造函数也属于默认构造函数)默认构造函数。

C++把变量分成两种:

  • 内置类型/基本类型:int、char、double、指针……
  • 自定义类型:class、struct去定义的类型对象

C++默认生成的构造函数对于内置类型成员变量不做处理,对于自定义类型的成员变量才会处理,这也就能很好的说明了为什么刚才没有对年月日进行处理(初始化),因为它们是内置类型(int类型的变量)

那么自定义类型的变量编译器该如何进行处理呢?

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

  首先,这是一个名为Time的类,有成员变量_hour,_minute,_second,并且还有一个无参的构造函数Time,对_hour,_minute,_second均初始化为0。接着:

class Date
{
public:
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
		cout << endl;
	}
private:
	// 基本类型(内置类型)
	int _year;
	int _month;
	int _day;
	// 自定义类型
	Time _t;
};
int main()
{
	Date d;
	return 0;
}

通过运行结果以及调试,也正验证了默认构造函数对自定义类型才会处理。这也就告诉我们,当出现内置类型时,就需要我们自己写构造函数了。 

如下情况的时候使用默认构造函数会更凸显出其价值 

class Stack
{
public:
	Stack()
	{
		_a = nullptr;
		_top = _capacity;
	}
private:
	int* _a;
	int _top;
	int _capacity;
 
};
 
class MyQueue 
{
public:
	//默认生成的构造函数就可以用了
	void push(int x)
	{}
 
	int pop() 
	{}
private:
	Stack _S1;
	Stack _s2;
};

 此时我队列里自定义类型_s1和_s2就不需要单独写初始化了,直接用默认的。但是如果栈里没有写构造函数,那么其输出的还是随机的,因为栈里的也是内置类型。就是一层套一层,下一层生效的前提是上一层地基打稳了。

总结:

  • 如果一个类中的成员全是自定义类型,我们就可以用默认生成的函数
  • 如果有内置类型的成员,或者需要显示传参初始化,那么都要自己实现构造函数。

三. 析构函数

3.1 析构函数概念 

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

3.2 析构函数特性 

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

2. 无参数无返回值。

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

4. 对象生命周期结束时,自动调用析构函数

~Date()
{
	cout << "~Date()" << endl;
}

带入示例再看看: 

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

 图解

 首先,我实例化出的d1会调用它的默认构造函数进行初始化,其次调用Print函数进行打印,出了作用域后又调用其析构函数,这也就是为什么输出结果会是~Date()

我们知道当一个类对象销毁时,其中的局部变量也会随着该对象的销毁而销毁,例如,我们用日期类创建了一个对象d1,当d1被销毁时,对象d1当中的局部变量_year/_month/_day也会被编译器销毁。
 但是这并不意味着析构函数没有什么意义。像栈(Stack)这样的类对象,malloc、new、fopen出的空间,当该对象被销毁时,其中动态开辟的栈并不会随之被销毁,需要我们对其进行空间释放,这时析构函数的意义就体现了。

5. 编译器生成的默认析构函数,对会自定类型成员调用它的析构函数

class Time
{
public:
 ~Time()
 {
 cout << "~Time()" << endl;
 }
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;

// 程序运行结束后输出:~Time()

 return 0;
}

在main方法中根本没有直接创建Time类的对象,为什么最后会调用Time类的析构函数?

    main方法中创建了Date对象d,而d中包含4个成员变量,其中_year, _month, _day三个是内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可; 而_t是Time类对象,所以在d销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数。
    但是: main函数中不能直接调用Time类的析构函数,实际要释放的是Date类对象, 所以编译器会调用Date类的析构函数,而Date没有显式提供,则编译器会给Date类生成一个默认的析构数,目的是在其内部调用Time类的析构函数,即当Date对象销毁时,要保证其内部每个自定义对象都可以正确销毁,main函数中并没有直接调用Time类析构函数,而是显式调用编译器为Date类生成的默认析构函数
// 注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数,就是要销毁Time类的对象_t,就要调用Time类的析构函数
题目:用类实例化出st1和st2两个对象,首先,st1肯定先构造,st2肯定后构造,这点毋庸置疑,那关键是谁先析构呢?
int main()
{
	Stack st1;
	Stack st2;
}

答案:st2先析构,st1后析构

解析:这里st1和st2是在栈上的,建立栈帧,其性质和之前一样,后进先出,st2后压栈,那么它肯定是最先析构的。所以栈里面定义对象,析构顺序和构造顺序是反的。

 若自己没有定义析构函数,虽说系统会自动生成默认析构函数,不过也是有要求的,和构造函数一样,内置类型不处理,自定义类型会去调用它的析构函数,如下:

总结 : 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如 Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。

四. 拷贝构造函数

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

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

相关文章

架构师系列-搜索引擎ElasticSearch(七)- 集群管理之分片

集群健康检查 Elasticsearch 的集群监控信息中包含了许多的统计数据&#xff0c;其中最为重要的一项就是集群健康&#xff0c;它在 status字段中展示为 green&#xff08;所有主分片和副本分片都正常&#xff09;、yellow&#xff08;所有数据可用&#xff0c;有些副本分片尚未…

EEG-GCNN 论文问题整理

auc是什么&#xff1f; AUC是指接收者操作特征曲线&#xff08;ROC曲线&#xff09;下的面积&#xff0c;用于评估分类模型的性能。AUC的取值范围在0到1之间&#xff0c;越接近1表示模型的性能越好&#xff0c;越接近0.5表示模型的性能越差。AUC的计算方法是通过计算ROC曲线下…

Kafka分布式数据处理平台

目录 一.消息队列基本介绍 1.为什么需要消息队列 2.使用消息队列的好处 2.1 解耦 耦合&#xff08;非解耦&#xff09; 解耦 2.2 可恢复性 2.3 缓冲 2.4 灵活性 & 峰值处理能力 2.5 异步通信 3.消息队列的两种模式 3.1 点对点模式 3.2 发布/订阅模式 二.Kafk…

【脚本】多功能Ubuntu临时授予用户sudo权限管理工具

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 设计原理和初衷可以看这里&#xff1a;【技巧】Ubuntu临时授予用户sudo权限&#xff0c;并在一定时间后自动撤销_ubuntu jianshao sudo-CSDN博客文章浏览阅读404次。非常实用_ubuntu jianshao sudohttps://blog.c…

# 达梦sql查询 Sql 优化

达梦sql查询 Sql 优化 文章目录 达梦sql查询 Sql 优化注意点测试数据单表查询 Sort 语句优化优化过程 多表关联SORT 优化函数索引的使用 注意点 关于优化过程中工具的选用&#xff0c;推荐使用自带的DM Manage&#xff0c;其它工具在查看执行计划等时候不明确在执行计划中命中…

Echarts简单的多表联动效果和添加水印和按钮切换数据效果

多表联动 多表联动效果指的是在多个表格之间建立一种交互关系&#xff0c;以便它们之间的操作或选择能够相互影响。通常情况下&#xff0c;多表联动效果可以通过以下方式之一实现&#xff1a; 数据关联&#xff1a; 当在一个表格中选择或操作某些数据时&#xff0c;另一个表格…

【opencv】示例-stiching.cpp 图像拼接

#include "opencv2/imgcodecs.hpp" // 导入opencv图像编码功能库 #include "opencv2/highgui.hpp" // 导入opencv高层用户界面功能库 #include "opencv2/stitching.hpp" // 导入opencv图像拼接功能库#include <iostream> // 导入输入输出…

Tool:VRAM的简介、查询电脑VRAM的常用方法

Tool&#xff1a;VRAM的简介、查询电脑VRAM的常用方法 目录 VRAM的简介 查询电脑VRAM的常用方法 1、对于Windows系统 T1、设置-系统-显示查询法 T2、使用 DirectX 诊断工具&#xff1a; T3、使用系统信息工具&#xff1a; 2、对于Linux系统 T1、使用nvidia-smi命令&…

IO流基本流

目录 什么是IO流 IO流的体系 字节流 FileOutputStream FileInputStream 字符集 字符流 FileReader FileWriter 字节流和字符流的使用场景 什么是IO流 内存不能永久化存储,程序停止,数据丢失,所以要添加一个存档功能,存储到硬盘的文件,我们要知道文件在哪里和如何传…

Java实现二叉树(下)

1.前言 http://t.csdnimg.cn/lO4S7 在前文我们已经简单的讲解了二叉树的基本概念&#xff0c;本文将讲解具体的实现 2.基本功能的实现 2.1获取树中节点个数 public int size(TreeNode root){if(rootnull){return 0;}int retsize(root.left)size(root.right)1;return ret;}p…

Python 全栈 Web 应用模板:成熟架构,急速开发 | 开源日报 No.223

tiangolo/full-stack-fastapi-template Stars: 15.6k License: MIT full-stack-fastapi-template 是一个现代化的全栈 Web 应用模板。 使用 FastAPI 构建 Python 后端 API。使用 SQLModel 进行 Python SQL 数据库交互&#xff08;ORM&#xff09;。Pydantic 用于数据验证和设…

excel里如何的科学计数法的数字转换成数值?

比如下图&#xff0c;要想把它们转换成3250跟1780&#xff0c;有什么快捷的办法吗&#xff1f; 科学计数法在excel里的格式&#xff0c;与我们常规在数学上写的有差异。这个转换可以这样做&#xff1a; 1.转换后的效果&#xff1a; 2.问题分析 题目中所附截图&#xff0c;单元…

HTML重要标签重点及属性(表格表单列表)——之转生在异世界学前端

表格标签 table是用于定义表格的标签 tr是用于定义表格的行 td是用来定义表格的列&#xff0c;th是表头一般只有一个表头会加粗 表格属性border是设置边框值为1;1是有边框&#xff0c; align设置居中对齐方式center&#xff0c;left&#xff0c;right cellpadding设置文字…

Linux操作系统中关于用户管理的操作

创建新用户 useradd 【选项】 用户名 在/etc/passwd中以追加的方式在passwd的最后一行添加用户信息。 可以使用命令tail -n 1/etc/passwd查看文件的最后一行内容。 ls /home/首先/home/这是普通用户的家目录&#xff0c; 在/home/下会有一个跟用户名同名的家目录&#xf…

Arrow, 一个六边形的 Python 时间库

文章目录 Arrow, 一个六边形的 Python 时间库第一部分&#xff1a;背景介绍第二部分&#xff1a;库是什么&#xff1f;第三部分&#xff1a;如何安装这个库&#xff1f;第四部分&#xff1a;库函数使用方法第五部分&#xff1a;场景应用第六部分&#xff1a;常见Bug及解决方案第…

Mac 软件清单

~自留备用~ Macbook用了几年之后, 512G的内置硬盘有些紧张了, 这几天总是提示空间不足, 就重装了下系统, 重装之后竟然不记得有些软件的名字和下载链接, 特此记录 Office 办公套件 直接从微软官网下载Office 安装包https://officecdnmac.microsoft.com/pr/C1297A47-86C4-4C1F…

前端三剑客 —— JavaScript (第六节)

目录 内容回顾 BOM编程 DOM编程* document对象 document对象的属性 document对象的方法 DOM对象节点 操作DOM对象内容 操作DOM对象的属性 --- DOM对象.属性名称 --- DOM对象[属性名称] --- 调用系统API &#xff08;Application Program interface&#xff09;&#…

汇编语言知识点整理(应付考试专用,想学习找其他的)

1 基础知识 1.1 信息在计算机内部的表示和存储 1.1.1 信息存储的基本概念 信息在计算机内部是以二进制数据的形式在存储器中存取的。介绍两个基本概念&#xff1a; 位&#xff08;Bit&#xff09; 计算机中最小的数据单位&#xff0c;一位有0、1两状态。Bit是计算机中最小…

对常见FTP客户端/服务器的调查与分析

前言 主要是想看看常见的服务器和客户端是如何实现协议中要求的功能的&#xff0c;。 比如RF959要求的记录结构&#xff08;Record Structure&#xff09;、页结构&#xff08;Page Structure&#xff09;、Block Mode、Compress Mode&#xff0c;看起来就很抽象。 实测发现…

【算法】回溯:与递归,dfs的同质与分别,剪枝与恢复现场的详细理解,n皇后的回溯解法及算法复杂度分析。

目录 ​编辑 1.什么是回溯 2.关于剪枝 3.关于恢复现场 4.题目&#xff1a;二叉树的所有路径&#xff08;凸显恢复现场&#xff1a;切实感受回溯与深搜&#xff09; 问题分析 ①函数设置为&#xff1a;void Dfs(root) ②函数设置为&#xff1a;void Dfs(root,path) 解题思想&…