【C++】:类和对象(中)之类的默认成员函数——构造函数and析构函数

news2024/11/7 9:39:07

在这里插入图片描述

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

如果一个类中什么成员都没有,简称为空类
空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数

class Date {};

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
由于编译器的优化 我们未给_a赋值 这里是不会报错的
这里还需要强调一个点就是成员函数的地址不在对象中
成员变量存放在对象中

2. 构造函数

我们先来看两道经典的面试题
在这里插入图片描述
这道题首先排除A 因为nullptr属于运行错误 我们编译器在编译的时候只负责检查语法错误 nullptr语法是没错误的
这道题是正常运行的 但是为什么呢?
这是因为成员函数的地址不在对象中
成员变量存放在对象中
第二题
在这里插入图片描述
这里选择B运行崩溃
因为我们这里涉及到一个this指针的知识
this指针为nullptr 所以nullptr指向_a就会报错

2.1 概念

对于以下Date类:

class Date
{
public:
 void Init(int year, int month, int day)
 {
  _year = year;
 _month = month;
 _day = day;
 }
 void Print()
 {
 cout << _year << "-" << _month << "-" << _day << endl;
 }
private:
 int _year;
 int _month;
 int _day;
};
int main()
{
 Date d1;
 d1.Init(2022, 7, 5);
 d1.Print();
 Date d2;
 d2.Init(2022, 7, 6);
 d2.Print();
 return 0;
}

对于Date类,可以通过 Init 公有方法给对象设置日期,但如果每次创建对象时都调用该方法设置信息,未免有点麻烦,那能否在对象创建时,就将信息设置进去呢?
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次

2.2 特性

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

  1. 函数名与类名相同
  2. 无返回值
  3. 对象实例化时编译器自动调用对应的构造函数
  4. 构造函数可以重载
 class Date
 {
  public:
      // 1.无参构造函数
      Date()
     {}
  
      // 2.带参构造函数
      Date(int year, int month, int day)
     {
          _year = year;
          _month = month;
          _day = day;
     }
  private:
      int _year;
      int _month;
      int _day;
 };
  
  void TestDate()
 {
      Date d1; // 调用无参构造函数
      Date d2(2015, 1, 1); // 调用带参的构造函数
  
      // 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
      // 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象
      // warning C4930: “Date d3(void)”: 未调用原型函数(是否是有意用变量定义的?)
      Date d3();
 }
  1. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成
    代码演示和注释如下
class date
{
public:
	// 够成函数重载,但是无参调用存在歧义,不能同时存在
	/*date()
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}*/

	date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	//void init(int year, int month, int day)
	//{
	//	_year = year;
	//	_month = month;
	//	_day = day;
	//}

	void print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

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

// date f(); 函数声明
int main()
{
	date d1;
	d1.print();

	date d2(2023, 10, 19);
	d2.print();

	date d3(2023, 10);
	d3.print();

	return 0;
}

在这里插入图片描述
6. 关于编译器生成的默认成员函数,很多童鞋会有疑惑:不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d对象_year/_month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用??
解答:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char…,自定义类型就是我们使用class/struct/union等自己定义的类型,看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数
大家可曾记得我前面的LeetCode刷题里面讲过一道题就是两个栈实现一个队列吗 我们构造函数现在就要用这道题来进行验证

class Date
{
public:
	// 够成函数重载,但是无参调用存在歧义,不能同时存在
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

	// 默认生成的构造函数,啥事都干?
	// 处理自定义(会去调用这个成员的默认构造函数)
	// 内置类型不确定(看编译器),建议当成不处理
private:
	// C++11支持,声明时给缺省值
	int _year = 1;
	int _month = 1;
	int _day = 1;
};

class Stack
{
public:
	Stack(size_t capacity = 3)
	{
		cout << "Stack(size_t capacity = 3)" << endl;

		_a = (int*)malloc(sizeof(int) * capacity);
		if (nullptr == _a)
		{
			perror("malloc申请空间失败!!!");
		}

		_capacity = capacity;
		_top = 0;
	}

private:
	int* _a;
	int _capacity;
	int _top;
};

// 两个栈实现一个队列
class MyQueue
{
private:
	Stack _pushst;
	Stack _popst;
	int _size = 1;
};

int main()
{
	Date d1;
	d1.Print();

	Stack st1;

	MyQueue mq;

	return 0;
}

在这里插入图片描述
那如果我将栈的初始化那部分注释掉了会发生什么呢?
在这里插入图片描述
注释掉了编译器不会默认去调用构造函数进行打印的 希望大家能够理解
这里再给大家强调一下 看图片解释
在这里插入图片描述
注意:C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值

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

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	// C++11支持,声明时给缺省值
	int _year = 1;
	int _month = 1;
	int _day = 1;
};

// 1、我们不写编译默认生成那个构造函数,叫默认构造
// 2、无参构造函数也可以叫默认构造
// 3、全缺省也可以叫默认构造
// 可以不传参数就调用构造,都可以叫默认构造
// 这三个函数不能同时存在,只能存在一个

int main()
{
	Date d1;
	d1.Print();

	return 0;
}
  1. 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个
    注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数
class Date
{
public:
 Date()
 {
 _year = 1900;
 _month = 1;
 _day = 1;
 }
 Date(int year = 1900, int month = 1, int day = 1)
 {
 _year = year;
 _month = month;
 _day = day;
 }
private:
 int _year;
 int _month;
 int _day;
};
// 以下测试函数能通过编译吗?
void Test()
{
 Date d1;

3.析构函数

3.1 概念

通过前面构造函数的学习,我们知道一个对象是怎么来的,那一个对象又是怎么没呢的?
析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作

3.2 特性

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

  1. 析构函数名是在类名前加上字符 ~
  2. 无参数无返回值类型
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数
class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

	~Date()
	{
		// Date严格来说,不需要写析构函数
		cout << "~Date()" << endl;
	}
private:
	// C++11支持,声明时给缺省值
	int _year = 1;
	int _month = 1;
	int _day = 1;
};
  1. 关于编译器自动生成的析构函数,是否会完成一些事情呢?下面的程序我们会看到,编译器生成的默认析构函数,对自定类型成员调用它的析构函数
class Stack
{
public:
	Stack(size_t capacity = 3)
	{
		cout << "Stack(size_t capacity = 3)" << endl;

		_a = (int*)malloc(sizeof(int) * capacity);
		if (nullptr == _a)
		{
			perror("malloc申请空间失败!!!");
		}

		_capacity = capacity;
		_top = 0;
	}

	~Stack()
	{
		cout << "~Stack()" << endl;

		free(_a);
		_capacity = _top = 0;
		_a = nullptr;
	}

private:
	int* _a;
	int _capacity;
	int _top;
};

class MyQueue
{
	// 默认生成析构函数,行为跟构造类似
	// 内置类型成员不做处理
	// 自定义类型成员会去调用他的析构
private:
	Stack _pushst;
	Stack _popst;
	int _size = 1;
};
int main()
{
	//Date d1;
	//Stack st1;
	MyQueue mq;

	return 0;
}
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;
 return 0;
}
// 程序运行结束后输出:~Time()
// 在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类生成的默认析构函数
// 注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数

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

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

相关文章

系统设计 - 我们如何通俗的理解那些技术的运行原理 - 第一部分:通信协议(2)

本心、输入输出、结果 文章目录 系统设计 - 我们如何通俗的理解那些技术的运行原理 - 第一部分&#xff1a;通信协议&#xff08;2&#xff09;前言SOAP vs REST vs GraphQL vs RPC代码优先与 API 优先HTTP 状态代码API 网关有什么作用步骤说明 我们如何设计有效和安全的 API弘…

Nautilus Chain 与 Coin98 生态达成合作,加速 Zebec 生态亚洲战略进程

目前&#xff0c;行业内首个模块化Layer3架构公链Nautilus Chain已经上线主网&#xff0c;揭示了模块化区块链领域迎来了全新的进程。在主网上线后&#xff0c;Nautilus Chain将扮演Zebec生态中最重要的底层设施角色&#xff0c;并将为Zebec APP以及Zebec Payroll规模性的采用提…

驱动day2作业

编写应用程序控制三盏灯亮灭 head.h #ifndef __HEAD_H__ #define __HEAD_H__ #define PHY_LED1_MODER 0x50006000 #define PHY_LED2_MODER 0x50007000 #define PHY_LED1_ODR 0x50006014 #define PHY_LED2_ODR 0x50007014 #define PHY_RCC 0x50000A28#endif demo1.c #includ…

海外调查问卷赚钱是真的吗?

海外问卷赚钱是真实的吗&#xff1f;我是橙河网络&#xff0c;一家问卷公司的老板&#xff0c;做这个行业已经2年时间了&#xff0c;首先给大家一个明确的回答&#xff1a;海外问卷调查赚钱是真实的&#xff01; 海外问卷调查项目&#xff0c;在国内已经存在一二十年的时间了&…

21.3 Python 使用DPKT分析数据包

dpkt项目是一个Python模块&#xff0c;主要用于对网络数据包进行解析和操作。它可以处理多种协议&#xff0c;例如TCP、UDP、IP等&#xff0c;并提供了一些常用的网络操作功能&#xff0c;例如计算校验和、解析DNS数据包等。由于其简单易用的特性&#xff0c;dpkt被广泛应用于网…

[C语言]排序的大乱炖——喵喵的成长记

宝子&#xff0c;你不点个赞吗&#xff1f;不评个论吗&#xff1f;不收个藏吗&#xff1f; 最后的最后&#xff0c;关注我&#xff0c;关注我&#xff0c;关注我&#xff0c;你会看到更多有趣的博客哦&#xff01;&#xff01;&#xff01; 喵喵喵&#xff0c;你对我真的很重要…

C# Onnx Detic 检测2万1千种类别的物体

效果 lable organism benthos heterotroph cell person animal plant food artifact hop check-in dressage curvet piaffe funambulism rock_climbing contact_sport outdoor_sport gymnastics acrobatics track_and_field track jumping broad_jump high_jump Fosbury_flop …

adb 获取 Android 设备中已安装的 apk 文件

前言 今天发现手机上一个应用在应用商店已经搜索不到了&#xff0c;想把其推荐给朋友使用&#xff0c;发现不知道从哪里找原始的 apk 安装文件&#xff0c;记录一下。 如何提取 apk 两种方法 MT管理器导出 可以使用 MT管理器(Android 平台逆向神器)&#xff0c;它有个 安装…

新版网站云监控计划任务系统源码+定时访问指定网址监控/更新(PHP源码)

源码简介&#xff1a; 新版网站云监控计划任务系统源码&#xff0c;能够定时访问网址监控&#xff0c;是更新的版本。该源码可以提供高效稳定的监控服务&#xff0c;免费简单使用并容易上手&#xff0c;这个为最新接口&#xff0c;能让用户更放心地进行任务监控。跟其他云任务…

SystemVerilog Assertions应用指南 Chapter1.37 使用局部变量的SVA

在序列或者属性的内部可以局部定义变量,而且可以对这种变量进行赋值。变量接着子序列放置,用逗号隔开。如果子序列匹配,那么变量赋值语句执行。每次序列被尝试匹配时,会产生变量的一个新的备份。 module cubed(enable1, a, aa, clk);input logic [7:0] a; input logic enable1,…

【力扣刷题】回文链表、环形链表、合并两个有序链表

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaEE 操作系统 Redis 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 刷题篇 一、回文链表1.1 题目描述1.2 思路分…

顺序队列----数据结构

队列的概念 队列&#xff0c;符合先进先出特点的一种数据结构&#xff0c;是一种特殊的线性表&#xff0c;但它不像线性表一样可以任意插入和删除操作&#xff0c;而是只允许在表的一端插入&#xff0c;也就是在队列的尾部进行插入&#xff1b;只允许在表的另一端进行删除&…

Vue2之防抖_debounce封装函数v-debounce自定义指令(传参/不传)

目录 1、防抖 2、debounce - 封装函数 3、v-debounce 全局自定义指令 1、防抖 推荐文章 &#xff1a; https://blog.csdn.net/weixin_58099903/article/details/119902796 2、debounce - 封装函数 utils / tools.js /*** 函数防抖 是n秒后延迟执行&#xff0c;多用于页面scr…

js获取视频编码

一.背景 有些浏览器不支持某些视频的编码方式导致播放出现问题&#xff0c;这个时候要限制视频上传 二.插件 https://unpkg.com/mediainfo.js0.1.4/dist/mediainfo.min.js 三.完整html代码 <!DOCTYPE html> <html lang"en"> <head><meta ch…

【微服务保护】Sentinel 流控规则 —— 深入探索 Sentinel 的流控模式、流控效果以及对热点参数进行限流

文章目录 前言一、快速掌握 Sentinel 的使用1.1 什么是簇点链路1.2 Sentinel 的简单使用示例 二、Sentinel 流控模式2.1 直接模式2.2 关联模式2.3 链路模式 三、流控效果3.1 快速失败3.2 预热模式3.3 排队等待 四、对热点参数的流控4.1 热点规则4.2 热点规则演示 前言 微服务架…

flutter doctor检测环境,出现CocoaPods installed but not working

1. 安装flutter, 地址: 安装和环境配置 - Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 2. 安装成功后&#xff0c;通过flutter doctor检测环境。以mac为例&#xff0c;出现了CocoaPods installed but not working 错误提示时&#xff0c;以下为解决方案: 2.1 rvm i…

AJAX: 对话框大全

AJAX:$.ajax({url: "/admin/cutting/getDataWeek",type: "GET",data:{},dataType:json,success: function (res) {if (res.code 1) {}},error:function (error) {console.log(请求失败);console.log(error);}}); $(.sub).unbind(click).click(funct…

浅析云数据安全的必要性

随着企业和个人用户越来越多地将数据存储和处理移到云上&#xff0c;云数据安全变得至关重要。云计算的发展为我们提供了卓越的灵活性和可扩展性&#xff0c;但也伴随着潜在的风险。在这个信息高度互联的时代&#xff0c;保护敏感数据是一项迫切的任务。本文将探讨云数据安全的…

microcom串口调试工具使用

microcom串口助手使用介绍 microcom是一个在终端中使用的串口助手&#xff0c;类似平常使用SSCOM一样的东西&#xff0c;不过是在终端中使用而已。 使用的是busybox构建的文件系统 microcom源码路径&#xff1a;busybox/miscutils/microcom.c microcom 参数&#xff1a; [r…

QT计时器

widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTimerEvent> //计时器类 #include <QTime> //时间类 QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widg…