C++的类与对象(三):构造函数、析构函数、对象的销毁顺序

目录

类的6个默认成员函数

构造函数

语法

特性

析构函数

特性

对象的销毁顺序​​​​​​​​​​​​​​


类的6个默认成员函数

问题:一个什么成员都没的类叫做空类,空类中真的什么都没有吗?

基本概念:任何类在什么都不写时,编译器会自动生成以下六个默认成员函数(无参的)

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

注意事项:如果我们自己实现了这些函数,那么编译器就不会生成默认的成员函数 

构造函数

产生原因:初始化容易被遗忘,且有时候会太麻烦

作用:完成初始化工作(Init)

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;
}

        对于Data类,之前我们会通过Init公有方法给对象设置日期,但每次创建对象时都需要调用该方法设置信息就显得有点麻烦,C++的构造函数就可以做到在对象创建时,就将信息设置进去

语法

1、构造函数是特殊的成员函数

2、构造函数的主要任务是在类实例化对象时初始化对象,而不是去开空间创建对象

3、构造函数的函数名与所在类的类名相同(类名是A,构造函数的名字就叫A,客随主便)

4、构造函数无返回值(不是void,而是连void都没)

5、对象实例化时编译器会自动调用合适的构造函数(对象后没括号就调无参构造函数,有实参就调用带参构造函数)

#include <iostream>
using namespace std;class Date{public:// 1.无参构造函数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();return 0;
}

6、构造函数可以重载

#include <iostream>
using namespace std;class Date
{
public:// 1.无参构造函数Date(){_year = 1;_month = 1;_day = 1;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}// 2.带参构造函数Date(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};int main()
{Date d1; // 调用无参构造函数d1.Print();Date d2(2015, 1, 1); // 调用带参的构造函数d2.Print();return 0;
}

7、构造参数没有形参时,对象实例化时不能在对象后加括号

8、 对象实例化时必须调用构造函数,没有或者没有合适的构造函数就会报错(d1原本想要调用无参的构造函数,但是只有带参的构造函数,有了带参的构造参数编译器就算像提供不带参的默认构造参数也没办法了)

#include <iostream>
using namespace std;class Date
{
public:void Print(){cout << _year << "-" << _month << "-" << _day << endl;}//带参构造函数Date(int year, int month, int day){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};int main()
{Date d1;d1.Print();return 0;
}

9、无参的构造函数和全缺省的构造函数在语法上因为函数重载可以同时存在,但是在实践中不可以,会造成调用歧义

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

特性

1(重点)、实例化对象时我们不写构造函数(包括默认构造函数和普通的构造函数),编译器就会生成默认构造函数,从而初始化对象,我们写了构造参数编译器就不会生成默认构造参数(普通的构造参数是指带参的构造参数)

2、编译器生成的默认构造参数什么也不干,为什么?(我们程序员没有定义一个构造参数是我们的问题,但是你编译器既然为我们自动生成了一个默认构造参数,你总得让它起点作用吧,就是只让成员变量初始化为0也行啊)

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

这是因为,C++将数据分为了内置(基本)类型和自定义类型:

  • 内置类型:语言自身定义的类型(int、char、double、任何类型的指针等)
  • 自定义类型:程序员根据自己需求定义的类型(struct、class等)

3、C++98规定编译器生成的默认构造函数(程序员没提过时),对内置类型不做处理,对自定义类型会去调用它的!!默认构造函数!!(一定是默认构造函数而不是普通的构造函数)

#include <iostream>
using namespace std;//编译器对自定义类型会调用它的默认构造
class A
{
public:A(){cout << "A()" << endl;_a = 0;}
private:int _a;};//编译器对内置类型不做处理
class Date
{
public://这里没有自定义构造函数,编译器默认生成构成函数void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;A _aa;
};int main()
{Date d1;d1.Print();return 0;
}

大致步骤如下:
①Date d1:实例化对象d1

②Data{}:编译器自动生成默认构造函数,并开始对成员变量开始初始化 

③__aa:_aa的类型是A类,属于自定义类型,调用A类的提供的!默认!构造函数

如果A类也没提供默认构造函数,那么_aa也是随机值(虽然编译器也可以为A提供但是没用):

#include <iostream>
using namespace std;class A
{
public:int getA() // 添加一个公共方法以获取 _a 的值{return _a;}private:int _a;};class Date
{
public:void Print(){cout << "Year: " << _year << ", Month: " << _month << ", Day: " << _day << endl;cout << "Value of '_aa' member variable in class 'Date': " << _aa.getA() << endl; // 打印_aa对象内部_a变量的值}private:int _year;int _month;int	_day;A _aa;
};int main()
{Date d1;d1.Print();return 0;
}

④_year、_month、_day:是int类型,属于内置类型,不做任何操作,结果为随机值

为什么在调试时,先初始化对象_aa?

答:C++中,类的成员变量初始化顺序是由它们在类中声明的顺序决定的,而不是由它们在构造函数初始化列表中出现的顺序决定

注意事项:有些新的编译器会对内置类型也做处理,但是C++的标准没有规定 

4、C++11规定内置类型的成员变量声明时可给默认值(为C++98打补丁,没给的依然是随机值)

#include <iostream>
using namespace std;class A
{
public:A(){cout << "A()" << endl;_a = 0;}private:int _a;};class Date
{
public:void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private://声明时给缺省值int _year = 2024;int _month =3 ;int _day;A _aa;
};int main()
{Date d1;d1.Print();return 0;
}

5、 只有无参 / 全缺省构造函数、编译器默认生成的构造函数都叫默认构造函数,其余的都是构造函数而不是默认构造函数,默认构造函数只能有一个 

//默认构造参数只能有一个:
#include <iostream>
using namespace std;
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;
};int main()
{Date d1;return 0;
}

//成员变量在声明时未给缺省值
#include <iostream>
using namespace std;
class Date
{
public:Date(int year = 1, int month , int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}~Date(){cout << this << endl;cout << " ~Date()" << endl;}private:int _year;int _month;int _day;};int main()
{Date d1;d1.Print();return 0;
}

//成员变量在声明时提供缺省值:
#include <iostream>
using namespace std;
class Date
{
public:Date(int year = 1, int month , int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}~Date(){cout << this << endl;cout << " ~Date()" << endl;}private:int _year = 1;int _month = 1;int _day =1;
};int main()
{Date d1;d1.Print();return 0;
}

为什么成员变量在声明时已经给了缺省值,为什么Data还要再给?

虽然为 Date 类中的私有成员变量 _year_month, 和 _day 提供了默认值(1, 1, 1),但这些缺省值仅在默认构造函数未初始化成员变量时才会被用于初始化成员变量。实例化d1时,类中定义了一个貌似是全缺省的构造函数,因此编译器不会自动生成默认构造函数(那个位置上已经有人了,只是那个人有点缺陷),且因为该貌似的全缺省构造函数缺少两个缺省值所以报错

#include <iostream>
using namespace std;class Time
{
public://带参构造函数,不是默认构造函数Time(int hour){cout << "Time()" << endl;_hour = 0;_minute = 0;_second = 0;}
private:int _hour;int _minute;int _second;
};class Date
{
private://声明给缺省值int _year;int _month;int _day;Time _t;
};int main() 
{Date d;return 0;
}

为什么会提示_t没有默认构造函数?

答:实例化对象d时,Date类没给构造函数,编译器自动生成默认构造函数(无参),_year、_month、_day是内置类型不做处理,_t是自定义类型会调用Time类的默认构造函数,但是Time类中已经有了一个带参的构造函数(普通构造函数)所以Time类中不会有默认构造函数了(只要我们写了构造函数,即使它是带参的普通构造函数,编译器就不会给我们提供默认构造参数了)故报错

 结论:绝大多数场景下都需要自己实现构造函数,且更推荐用全缺省构造函数​​​​​​​

析构函数

 产生原因:需要人为销毁的空间容易忘记销毁,内存泄漏可能不会报错

#include <iostream>
using namespace std;
class Date
{
public:void Print(){cout << _year << "-" << _month << "-" << _day << endl;}~Date(){cout << this << endl;cout << " ~Date()" << endl;}private://声明给缺省值int _year = 1;int _month = 1;int _day = 1;};void func()
{Date d2;
}class Stack
{
public:Stack(size_t capacity = 4){_array = (int*)malloc(sizeof(int*) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}void Push(int data){// CheckCapacity();_array[_size] = data;_size++;}//~Stack()//{//    cout << "~Stack()" << endl;//    if (_array)//    {//        free(_array);//        _array = nullptr;//    }//    _size = _capacity = 0;//}private:int* _array;int _capacity;int _size;
};int main()
{func();Date d1;Stack st1; //内存泄漏return 0;
}

        如果Stack类中没有自定义的析构函数就会出现内存泄漏,因为我们即没有定义Destory函数去销毁在堆上开辟的空间,析构函数也不起作用

作用:完成清理工作(Destory)

基本概念:析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的,而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作

特性

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

2、析构函数无参数无返回值类型

3、一个类只能有一个析构函数,若未显示定义,编译器会自动生成默认的析构函数

4、析构函数不能重载(清理一遍数据就应该被清理完成)

5、对象生命周期结束(函数结束)时,程序会自动调用析构函数

#include <iostream>
using namespace std;
class Date
{
public:~Date(){cout << this << endl;cout << " ~Date()" << endl;}private://声明给缺省值int _year = 1;int _month = 1;int _day =1;
};void func()
{Date d2;//实例化对象d2
}//函数结束,调用析构函数销毁数据int main()
{func();  Date d1;//实例化对象d1return 0;
}//函数结束,调用析构函数销毁数据

6、编译器生成的析构函数,对内置成员变量不做处理,对自定成员变量会调用它的析构函数

#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;
}

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

答:main函数中创建了Date类的对象d,d中包含了四个成员变量,其中_year、_month、_day是内置类型的成员变量,销毁时不需要人为清理资源main函数结束后系统直接回收,而_t是Time类的对象,为了销毁_t就需要Data类的析构函数,但是我们并没有给Date类定义析构函数,所以编译器就会为Date类提供一个默认析构函数,该函数对于自定义类型的成员变量就会调用它(_t)的析构函数(Time提供的析构函数)

7、后定义的先析构(定义->开辟帧栈->压栈,栈后进先出)

#include <iostream>
using namespace std;
class Date
{
public:~Date(){cout << this << endl;cout << " ~Date()" << endl;}private://声明给缺省值int _year = 1;int _month = 1;int _day = 1;};class Stack
{
public:Stack(int capacity = 4){_array = (int*)malloc(sizeof(int*) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}void Push(int data){// CheckCapacity();_array[_size] = data;_size++;}~Stack(){cout << this << endl;cout << "~Stack()" << endl;if (_array){free(_array);_array = nullptr;}_size = _capacity = 0;}
private:int* _array;int _capacity;int _size;
};int main()
{Date d1;Stack st1;return 0;
}

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

 9、自定义类型的本质还是内置类型

对象的销毁顺序

从左至右:局部变量(后定义的先析构)——>局部的静态——>全局(后定义的先析构)

当成栈来理解,先进后出 

#include <iostream>
using namespace std;
class Date
{
public://全缺省构造函数Date(int year = 1){_year = year;}~Date(){cout << " ~Date()->" << _year << endl;}private://声明给缺省值int _year;int _month;int _day;};Date d5(5);//全局
Date d6(6);//全局
static Date d7(7);//全局
static Date d8(8);//全局void func()
{Date d3(3);//局部static Date d4(4);//局部的静态
}int main() 
{Date d1(1);//局部Date d2(2);//局部func();return 0;
}

#include <iostream>
using namespace std;
class Date
{
public://全缺省构造函数Date(int year = 1){_year = year;}~Date(){cout << " ~Date()->" << _year << endl;}private:int _year;int _month;int _day;};int main()
{Date d1(1);//局部Date d2(2);//局部static Date d3(3);//局部的静态return 0;
}

~over~

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

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

相关文章

软考73-上午题-【面向对象技术2-UML】-UML中的图4

一、构件图&#xff08;组件图&#xff09; 1-1、构件图的定义 展现了&#xff0c;一组构件之间的组织和依赖。 构件图专注于系统的静态实现图。 构件图与类图相关&#xff0c;通常把构件映射为一个、多个类、接口、协作。 【回顾】&#xff1a; 类图展示了一组对象、接口、…

SpringCloud-SpringBoot读取Nacos上的配置文件

在 Spring Boot 应用程序中&#xff0c;可以使用 Spring Cloud Nacos 来实现从 Nacos 服务注册中心和配置中心读取配置信息。以下是如何在 Spring Boot 中读取 Nacos 上的配置文件的步骤&#xff1a; 1. 引入依赖 首先&#xff0c;在 Spring Boot 项目的 pom.xml 文件中添加 …

Android Kotlin知识汇总(一)编程语言

在 2019 年 Google I/O 大会上宣布今后将优先采用 Kotlin 进行 Android 开发。Kotlin 是一种富有表现力且简洁的编程语言&#xff0c;不仅可以减少常见代码错误&#xff0c;还可以轻松集成到现有应用中。如果您想构建 Android 应用&#xff0c;建议您从 Kotlin 开始着手&#x…

分享axios+signalr简单封装示例

Ajax Axios Axios 是一个基于 promise 网络请求库&#xff0c;作用于node.js 和浏览器中。 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生 node.js http 模块, 而在客户端 (浏览端) 则使用 XMLHttpRequests。 从浏览器创建 XMLHttpReque…

MySQL-QA-异常问题及解决方案(持续更新)

MySQL-Q&A(持续更新) 1.1 PID文件找不到 问题描述 错误详情&#xff1a; ERROR&#xff01;The server quit without updating PID file (/usr/local/mysql/data/localhost.localdomain.pid) 解决方案 首先排查配置文件&#xff0c;一般路径为&#xff1a;/etc/my.cnf 检查…

D2力扣滑动窗口系列

滑动窗口算法&#xff08;Sliding Window&#xff09;&#xff1a; 滑动窗口算法&#xff08;Sliding Window&#xff09;&#xff1a;在给定数组 / 字符串上维护一个固定长度或不定长度的窗口。可以对窗口进行滑动操作、缩放操作&#xff0c;以及维护最优解操作。 滑动操作&am…

【Ubuntu】gonme桌面的 gdm 和 lightdm 区别

总结&#xff1a;都可以 gdm: 【Gnome Display Manager】 完整&#xff0c;体积大 lightdm: 【Light Display Manager】 轻量

甜甜圈和贪吃蛇的后续

代码复现-项目复现 代码复现 云课五分钟-02第一个代码复现-终端甜甜圈C-CSDN博客 项目复现 云课五分钟-03第一个开源游戏复现-贪吃蛇-CSDN博客 不同的地图 加入班级和标识 循序渐进 这些案例都是来源网络&#xff0c;只是方便熟悉一下云课使用过程。 此部分学生掌握情况非…

OpenCV 图像的几何变换

一、图像缩放 1.API cv2.resize(src, dsize, fx0,fy0,interpolation cv2.INTER_LINEAR) 参数&#xff1a; ①src &#xff1a;输入图像 ②dsize&#xff1a;绝对尺寸 ③fx&#xff0c;fy&#xff1a;相对尺寸 ④interpolation&#xff1a;插值方法 2.代码演示 import cv2 …

携手亚信安慧AntDB,在数智化浪潮中乘风破浪

随着大数据时代的到来&#xff0c;对数据库的需求愈发强烈。在这一背景下&#xff0c;国产数据库逐渐崭露头角&#xff0c;亚信安慧AntDB作为重要的代表产品之一正积极参与到激烈的市场竞争中。亚信安慧AntDB不仅追求技术的革新和突破&#xff0c;同时也致力于满足用户日益增长…

AVCE - AV Evasion Craft Online 更新 8 种加载方式 - 过 WD 等

免责声明&#xff1a;本工具仅供安全研究和教学目的使用&#xff0c;用户须自行承担因使用该工具而引起的一切法律及相关责任。作者概不对任何法律责任承担责任&#xff0c;且保留随时中止、修改或终止本工具的权利。使用者应当遵循当地法律法规&#xff0c;并理解并同意本声明…

2023年中国高校大数据挑战赛D题参考论文发布(全网首发)

腾讯文档】2023年大数据挑战赛资料说明 https://docs.qq.com/doc/DSEpWUVFySm1ObFB0 基于数据分析的行业职业技术培训能力评价 摘要 中国是制造业大国&#xff0c;产业门类齐全&#xff0c;每年需要培养大量的技能娴熟的技术工人进入工厂。本文将基于题目给出的数据&#x…

Java——正则表达式详解

目录 Java正则表达式1、正则表达式语法1.1、基本的元字符1.2、数量元字符1.3、位置元字符1.4、特殊字符元字符1.5、回溯引用和前后查找1.6、大小写转换1.7、匹配模式 2、Java中的正则表达式2.1、概述2.2、获取匹配位置2.3、捕获组 3、匹配单个字符3.1、匹配纯文本3.2、匹配任意…

讲解linux下的Qt如何编译oracle的驱动库libqsqloci.so

1.需求 最近linux下的Qt项目中要连接oracle数据库&#xff0c;用户需要我们访问他们的oracle数据库&#xff0c;查询数据 2.遇到的问题 qt连接oracle数据库需要oracle的驱动库libqsqloci.so插件&#xff0c;需要编译下&#xff0c;之前没有编译过&#xff0c;看了网上的…

蓝桥杯真题讲解:异或和之和 (拆位、贡献法)

蓝桥杯真题讲解&#xff1a;异或和之和 &#xff08;拆位、贡献法&#xff09; 一、视频讲解二、正解代码 一、视频讲解 蓝桥杯真题讲解&#xff1a;异或和之和 &#xff08;拆位、贡献法&#xff09; 二、正解代码 //拆位考虑 #include<bits/stdc.h> #define endl &…

【c++】string类的使用及模拟实现

1.我们为什么要学习string类&#xff1f; 1.1 c语言中的字符串 我们先了解一下什么是OOP思想 OOP思想&#xff0c;即面向对象编程&#xff08;Object-Oriented Programming&#xff09;的核心思想&#xff0c;主要包括“抽象”、“封装”、“继承”和“多态”四个方面。 抽象…

JDBC和连接池

JDBC和连接池 大纲 JDBC连接数据库的方式JDBCUtils事务 具体案例 JDBC 需求&#xff1a;满足Java程序能对多个不同的数据库进行操作&#xff0c;而创建了一种接口&#xff0c;实现对数据库的规范 连接数据库的方式 1.方法1 先创建一个Driver对象&#xff0c;然后设置…

【RabbitMQ】RabbitMQ的交换机

交换机类型 在上文中&#xff0c;都没有交换机&#xff0c;生产者直接发送消息到队列。而一旦引入交换机&#xff0c;消息发送的模式会有很大变化&#xff1a;可以看到&#xff0c;在订阅模型中&#xff0c;多了一个exchange角色&#xff0c;而且过程略有变化&#xff1a; Pub…

Android bp构建引入其他模块头文件

最近做项目过程中经常遇到Android.bp文件引入其他模块的头文件库&#xff0c;总是记不住bp的写法&#xff0c;这里做个记录 1.产生头文件库 2.其他的模块应用引入头文件库 不由想起来老师的名句&#xff1a;好记忆不如烂笔头。

基于遗传算法改进的RBF神经网络流量控制,基于GA-RBF的流量预测

目录 完整代码和数据下载链接:基于遗传算法改进的RBF神经网络流量控制,基于GA-RBF的流量预测(代码完整,数据齐全)资源-CSDN文库 https://download.csdn.net/download/abc991835105/88937452 RBF的详细原理 RBF的定义 RBF理论 易错及常见问题 RBF应用实例,基于遗传算法改…