当前位置: 首页 > article >正文

【哇! C++】类和对象(三) - 构造函数和析构函数

        

目录

一、构造函数

1.1 构造函数的引入

1.2 构造函数的定义和语法

1.2.1 无参构造函数:

1.2.2 带参构造函数

1.3 构造函数的特性

1.4 默认构造函数

二、析构函数

2.1 析构函数的概念

2.2 特性


        如果一个类中什么成员都没有,简称为空类。

        空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。

        默认成员函数用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

class Date
{};

一、构造函数

1.1 构造函数的引入

        对于Date类,有如下程序:

#include<iostream>
using namespace std;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(2025, 3, 5)return 0;
}

         对于Date类,可以通过Init公有方法给对象设置日期。但有时也会出现忘记Init初始化函数,而直接Push。为避免忘记,也为了方便程序撰写,所以,C++规定了构造函数。

1.2 构造函数的定义和语法

        构造函数是一种特殊的成员函数。虽然叫构造,但是其任务不是开空间,而是初始化对象。其特征如下:

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

所以Date类构造函数可写为:

1.2.1 无参构造函数:

#include<iostream>
using namespace std;class Date
{
public:    Date(){_year = 1;_month = 1;_day = 1;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};int main()
{Date d1;d1.Print();//Date d2();//err//Date d1(2024, 1, 27);//err,没有与参数列表匹配的构造函数实例return 0;
}

运行结果为: 

        此外,C++规定,无参构造函数变量名后不能加小括号。Date d2();有可能是函数的声明,返回类型是Date,因为这个地方要写函数声明的话,小括号中是要加参数类型的。

1.2.2 带参构造函数

#include<iostream>
using namespace std;class Date
{
public:Date(){_year = 1;_month = 1;_day = 1;}Date(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(2025, 3, 5);return 0;
}

        构造函数可以函数重载,也可以改写为全缺省。

#include<iostream>
using namespace std;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 Print{cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};int main()
{Date d1;Date d2(2025, 3, 5);return 0;
}

        语法上,上述两个函数也可以同时存在,因为函数重载,函数名虽然相同,但参数不同。但是在调用Date d1;时,会产生歧义,编译器不知道要调用谁,是要调用无参的还是要调用全缺省的。所以一般情况下,我们不这样写。

1.3 构造函数的特性

        1.C++规定,对象定义(实例化)的时候,必须调用构造函数。

        2.构造函数是默认成员函数,默认成员函数的特征是:我们没有显示定义,编译器会自动生成一个无参的;如果写了,编译器就不会生成。

        但是,当我们这样写如下代码以后,编译器调用了默认生成的构造函数,但是什么也没干,没有初始化。

#include<iostream>
using namespace std;class Date
{
public:void func(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year = 1;int _month = 1;int _day;
};int main()
{Date d1;d1.Print();//A _aa;return 0;
}

运行过程为:

运行结果为:

        说明了使用编译器实现的默认构造函数出来初始化的数据是随机值,不是0。

        C++98规定,默认生成的构造函数,对于内置类型不做处理;对于自定义类型会直接调用他的默认构造函数。即编译器对生成默认了构造函数,对int不做处理,但对于自定义成员类型A aa;要调用A的构造函数。

        内置类型/基本类型       -       int/char/double/指针

        自定义类型                   -       struct/class

        C++11对这个语法进行补丁,在声明的位置给缺省值。即不给缺省值,编译器什么都不管;给缺省值,就默认生成了构造函数,用缺省值去完成初始化。在类中,默认了Date(){int _year = 1;int _month = 1;};

1.4 默认构造函数

#include<iostream>
using namespace std;class Date
{    
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year = 1;int _month = 1;int _day;
};int main()
{Date d1;return 0;
}

        当我们运行时发现上述代码报错:此处编译报错,提示没有默认构造函数可以用。

        很多人经常理解默认构造函数就是编译器生成的那一个。编译器默认生成的构造函数是默认构造函数,但只是其中之一。

        无参的构造函数全缺省构造函数也被称为默认构造函数,且默认构造函数有且只能有一个。 一般情况下,建议优选全缺省构造函数。

总结:不需要传参就可以调用的构造函数,都可以叫默认构造函数。

        所以,上述代码中,没有无参,也没有全缺省的默认构造函数,此时就需要编译器就要生成一个默认构造函数。

        但是编译器默认生成的构造函数是有条件的,基于默认成员函数的特性:即没有写显示定义的构造函数,编译器才会自动生成一个无参的默认构造函数;一旦用户显示定义,编译器就不会再生成。

        为了使上述程序能够通过,需要函数重载,显式定义一个默认构造函数。

#include<iostream>
using namespace std;class Date
{    
public:Date(){_year = 1;_month = 1;_day = 1;}Date(int year, int month, int day){_year = year;_month = month;_day = day;}Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year = 1;int _month = 1;int _day;
};int main()
{Date d1;return 0;
}

二、析构函数

2.1 析构函数的概念

        内存泄漏是不会报错的,所以经常会忘记destory,所以C++就有了析构函数。

        析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。相当于destory,把动态开辟的数组空间free掉,不清理的话会出现内存泄漏。所以:构造函数完成的不是创建,析构函数完成的也不是销毁。

        在函数名前加~,在C语言中,~表示按位取反,所以选用这个符号就表示和构造函数的功能是相反的。

2.2 特性

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

  1. 析构函数名是类名前加字符“~”;
  2. 没有参数也没有返回值;
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载;
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

        第4点类似于构造函数,构造函数在实例化的时候会自动调用。

#include<iostream>
using namespace std;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类生成的默认析构函数。

        注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数。

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

#include<iostream>
using namespace std;class Stack
{
public:    Stack(int capacity = 3){cout << "Stack(int capacity = 3)" << endl;_arry = (int*)malloc(capacity * sizeof(4));if(_arry == NULL){perror("malloc");}_capacity = capacity;}void Push(int x){_arry[_size] = x;_size++;}~Stack(){cout << "~Stack()" << endl;free(_arry);_arry = NULL;_size = 0;_capacity = 0;}private:int* _arry;int _capacity = 0;int _size = 0;
};//注意必须要有分号class MyQueue
{
private:Stack st1;Stack st2;int _size = 0;
};//注意必须要有分号int main()
{MyQueue q;return 0;
}

运行结果为: 

2.3 析构顺序        

        销毁顺序为:局部对象(后定义的先析构)->局部的静态->全局对象(后定义的先析构)。程序证明如下:

class Date
{
public:Date(int year = 1){_year = year;}~Date(){cout << "~Date()->" << _year << endl;}
private:int _year ;int _month;int _day;
};void func()
{Date d3(3);static Date d4(4);
}Date d5(5);
static Date d6(6);int main()
{Date d1(1);Date d2(2);func();return 0;
}

        static Date d3(3);为局部静态,和上边两个的存储区域不同,第3是存在静态区的,虽然定义在了局部,但是生命周期是全局的。在main函数结束以后才会销毁。所以在这之前要先把main函数中的局部变量先销毁。

相关文章:

【哇! C++】类和对象(三) - 构造函数和析构函数

目录 一、构造函数 1.1 构造函数的引入 1.2 构造函数的定义和语法 1.2.1 无参构造函数&#xff1a; 1.2.2 带参构造函数 1.3 构造函数的特性 1.4 默认构造函数 二、析构函数 2.1 析构函数的概念 2.2 特性 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中…...

利用Ollama+AnythingLLM+本地向量数据库Milvus+本地DeepSeek大模型实现知识库的搭建

1. Ollama的搭建 基本介绍 Ollama是一个支持在Windows、Linux和MacOS上本地运行大语言模型的工具。它允许用户非常方便地运行和使用各种大语言模型,比如Qwen模型等。用户只需一行命令就可以启动模型。 Ollama 下载&#xff1a;https://ollama.com/download Ollama 官方主页&a…...

攻防世界WEB(新手模式)19-file_include

先进行代码分析 include("./check.php");&#xff1a;包含并执行当前目录下的check.php文件&#xff0c;通常用于引入一些通用的函数、类或配置信息。if(isset($_GET[filename]))&#xff1a;检查是否通过 GET 请求传递了名为filename的参数。如果传递了filename参数…...

Facebook 的隐私保护数据存储方案研究

Facebook 的隐私保护数据存储方案研究 在这个信息爆炸的时代&#xff0c;数据隐私保护已成为公众关注的热点。Facebook&#xff0c;作为全球最大的社交媒体平台之一&#xff0c;承载着海量用户数据&#xff0c;其隐私保护措施和数据存储方案对于维护用户隐私至关重要。本文将深…...

app UI自动化测试框架都包含哪些内容?

UI自动化测试框架是指用于自动化执行用户界面(UI)相关测试的工具和库。它们可以帮助开发团队提高测试效率、发现和解决应用程序中的问题&#xff0c;并确保应用程序的正确性和稳定性。下面将详细介绍一个完整的UI自动化测试框架应该具备的内容。 1. 测试环境配置 UI自动化测试框…...

C++ 中 `shared_ptr` 的用法及常见陷阱解析

shared_ptr 是 C 标准库&#xff08;<memory> 头文件&#xff09;中提供的一种智能指针&#xff0c;用于管理动态分配的内存。它通过引用计数&#xff08;reference counting&#xff09;来实现多个 shared_ptr 对象共享同一块内存的所有权。当最后一个 shared_ptr 被销毁…...

Android+SpringBoot的老年人健康饮食小程序平台

感兴趣的可以先收藏起来&#xff0c;还有大家在毕设选题&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;我会一一回复&#xff0c;希望帮助更多的人。 系统介绍 我将从经济、生活节奏、技术融合等方面入手&#xff0c;详细阐述居家养老管理模式兴起的…...

ORB-SLAM2源码学习(六):相机跟踪(局部地图跟踪和关键帧创建)

目录 1.局部地图跟踪 1.1 更新局部关键帧UpdateLocalKeyFrames 1.2 更新局部地图点&#xff08;来自局部关键帧&#xff09;UpdateLocalPoints() 1.3 投影匹配 2. 对比四种跟踪方式以及使用的投影匹配 3.关键帧创建 3.1 判断是否需要创建新关键帧: NeedNewKeyFrame() 3…...

视频格式有哪些视频格式汇总

视频格式是指视频文件的编码和封装方式&#xff0c;不同的格式适用于不同的应用场景。以下是一些常见的视频格式及其特点&#xff1a; 1. MP4&#xff08;MPEG-4 Part 14&#xff09; 特点&#xff1a;最流行的视频格式之一&#xff0c;支持多种视频编码标准&#xff08;如H.…...

【极客时间】浏览器工作原理与实践-2 宏观视角下的浏览器 (6讲) - 2.6 渲染流程(下):HTML、CSS和JavaScript,是如何变成页面的?

https://time.geekbang.org/column/article/118826 2.6 渲染流程&#xff08;下&#xff09;&#xff1a;HTML、CSS和JavaScript&#xff0c;是如何变成页面的&#xff1f; 2.5介绍了渲染流水线中的 DOM 生成、样式计算和布局三个阶段&#xff0c;2.6讲解渲染流水线后面的阶段…...

浏览器WEB播放RTSP

注意&#xff1a;浏览器不能直接播放RTSP&#xff0c;必须转换后才能播放。这一点所有的播放都是如此。 参考 https://github.com/kyriesent/node-rtsp-stream GitHub - phoboslab/jsmpeg: MPEG1 Video Decoder in JavaScript 相关文件方便下载 https://download.csdn.net…...

DeepSeek开源Day4:DualPipeEPLB技术详解

2 月 24 日&#xff0c;DeepSeek 启动 “开源周”&#xff0c;第四个开源的代码库为 DualPipe 与 EPLB&#xff08;一下发布了两个&#xff09;。DualPipe 与 EPLB 依然使用了大量与 Hopper 架构绑定的技术。 DualPipe 是由 DeepSeek-AI 团队开发的一种双向流水线并行通信算法&…...

阿里推出全新推理模型(因果语言模型),仅1/20参数媲美DeepSeek R1

阿里Qwen 团队正式发布了他们最新的研究成果——QwQ-32B大语言模型&#xff01;这款模型不仅名字萌萌哒(QwQ)&#xff0c;实力更是不容小觑&#xff01;&#x1f60e; QwQ-32B 已在 Hugging Face 和 ModelScope 开源&#xff0c;采用了 Apache 2.0 开源协议。大家可通过 Qwen C…...

vue实现一个pdf在线预览,pdf选择文本并提取复制文字触发弹窗效果

[TOC] 一、文件预览 1、安装依赖包 这里安装了disjs-dist2.16版本&#xff0c;安装过程中报错缺少worker-loader npm i pdfjs-dist2.16.105 worker-loader3.0.8 2、模板部分 <template><div id"pdf-view"><canvas v-for"page in pdfPages&qu…...

Unity自定义渲染管线(Scriptable Render Pipeline)架构设计与实现指南

一、SRP技术体系概述 1. 核心设计理念 全托管渲染控制&#xff1a;通过C#脚本完全掌控渲染流程 模块化架构&#xff1a;将渲染流程拆分为可组合的RenderPass GPU友好设计&#xff1a;支持CommandBuffer与Compute Shader混合编程 对惹&#xff0c;这里有一个游戏开发交流小组…...

时间复杂度分析与递归,以新南UNSW的COMP2521作业题为例

作者&#xff1a;Smooth&#xff08;连接教育高级讲师&#xff09; 首发于&#xff1a;⁠⁠⁠⁠⁠⁠⁠UNSW学习知识库&#xff08;UNSW Study Wiki&#xff09; 创作时间&#xff1a;2025年3月5日 如何测度算法的时间性能&#xff1f;理论分析Theoretical Analysis 测度算法时…...

RxJS与Redux革命性协同:打造高效、解耦的前端状态管理方案

摘要 本文探讨RxJS与Redux的融合应用&#xff0c;通过真实用户场景揭示其必要性&#xff0c;系统讲解整合策略&#xff0c;从基础到高级涵盖响应式编程优化、异步操作处理、状态流控制等核心主题。提供可落地的架构方案与性能优化技巧&#xff0c;包含10可复用代码示例和20实战…...

蓝桥杯P1259-奇怪的馈赠 (贪心题解)

题目&#xff1a;奇怪的捐赠 题目来源&#xff1a;1.奇怪的捐赠 - 蓝桥云课 题目描述 需要将 100 万&#xff08;1,000,000&#xff09;正好分成若干个 7 的次方形式的数&#xff08;如 7^01, 7^17, 7^249 等&#xff09;&#xff0c;且每种金额&#xff08;即每个 7 的次方…...

todo: 使用融云imserve做登录(android)

使用融云做登录注册思路 注册界面需要name, email, password考虑到融云注册用户的post格式 POST http://api.rong-api.com/user/getToken.json?userId1690544550qqcom&nameIronman这里的userId可以使用用户的email&#xff0c;但是要截断和 . 符号&#xff0c;即1690544…...

如何设置爬虫的User-Agent?

在爬虫开发中&#xff0c;设置合适的 User-Agent 是非常重要的一步。User-Agent 是 HTTP 请求头中的一个字段&#xff0c;用于标识客户端&#xff08;通常是浏览器&#xff09;的类型、版本、操作系统等信息。通过设置 User-Agent&#xff0c;可以模拟正常的浏览器访问行为&…...

C++ 二叉搜索树代码

C 二叉搜索树代码 #include <iostream> using namespace std;template<typename T> struct TreeNode{T val;TreeNode *left;TreeNode *right;TreeNode():val(0), left(NULL), right(NULL){}TreeNode(T x):val(x), left(NULL), right(NULL){} };template<typena…...

OpenAI Whisper:开启语音转文本的智能时代

在人工智能技术飞速发展的今天,OpenAI推出的Whisper语音识别系统正悄然改变着人类与机器的交互方式。作为一款开源的AI驱动语音转文本工具,Whisper凭借其跨语言能力、高精度识别和灵活的生态系统,成为开发者和普通用户共同追捧的技术标杆。 核心技术与突破 Whisper基于深度…...

基于CSDN资源,搭建AI赋能农业典型场景落地方案

农业场景&#xff0c;不但是信息化、自动化等薄弱的产业&#xff0c;更是AI落地困难的场景。基于此&#xff0c;想通过这篇文章查找一个CSDN相关资源&#xff0c;论证一下AI赋能农业三个典型场景的实现思路。 场景1&#xff1a;水质-土壤智能调控 **痛点&#xff1a;**水质恶…...

python量化交易——金融数据管理最佳实践——使用qteasy大批量自动拉取金融数据

文章目录 使用数据获取渠道自动填充数据QTEASY数据拉取功能数据拉取接口refill_data_source()数据拉取API的功能特性多渠道拉取数据实现下载流量控制实现错误重试日志记录其他功能 qteasy是一个功能全面且易用的量化交易策略框架&#xff0c; Github地址在这里。使用它&#x…...

RoboBrain:从抽象到具体的机器人操作统一大脑模型

25年2月来自北大、北京智源、中科院自动化所等的论文“RoboBrain: A Unified Brain Model for Robotic Manipulation from Abstract to Concrete”。 目前的多模态大语言模型&#xff08;MLLM&#xff09; 缺少三项必备的机器人大脑能力&#xff1a;规划能力&#xff0c;将复杂…...

DeepSeek本地接口调用(Ollama)

前言 上篇博文&#xff0c;我们通过Ollama搭建了本地的DeepSeek模型&#xff0c;本文主要是方便开发人员&#xff0c;如何通过代码或工具&#xff0c;通过API接口调用本地deepSeek模型 前文&#xff1a;DeepSeek-R1本地搭建_deepseek 本地部署-CSDN博客 注&#xff1a;本文不仅…...

数据库索引的作用:提升数据检索效率的关键

在数据库管理系统中&#xff0c;数据如同浩瀚海洋中的宝藏&#xff0c;如何快速准确地找到所需信息&#xff0c;成为了一个关键问题。这时候&#xff0c;数据库索引就如同一张精确的航海图&#xff0c;指引着我们高效地定位数据。那么&#xff0c;数据库索引究竟是什么&#xf…...

高效便捷的 Spring Boot 通用控制器框架

✨高效便捷的 Spring Boot 通用控制器框架✨ 一、简介 在 Java 开发中&#xff0c;重复性的基础接口编写工作常令人头疼。本框架基于 Spring Boot 与 MyBatis-Plus&#xff0c;精心构建通用控制器类BaseController&#xff0c;旨在为开发者排忧解难&#xff0c;极大减少繁琐的…...

SQL_语法

1 数据库 1.1 新增 create database [if not exists] 数据库名; 1.2 删除 drop database [if exists] 数据库名; 1.3 查询 (1) 查看所有数据库 show databases; (2) 查看当前数据库下的所有表 show tables; 2 数据表 2.1 新增 (1) 创建表 create table [if not exists…...

在 CentOS 上,常用几种方法来确保 Python 脚本在断开终端后继续运行

在 CentOS 上&#xff0c;你可以使用以下几种方法来确保 Python 脚本在断开终端后继续运行&#xff1a; 1. 使用 nohup 命令 nohup 命令可以让进程在终端关闭后继续运行。 nohup python main.py > output.log 2>&1 &nohup&#xff1a;忽略挂断信号&#xff0c…...