C++——类和对象(2):构造函数、析构函数、拷贝构造函数

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

        我们将什么成员都没有的类称为空类,但是空类中并不是什么都没有。任何类中都会存在6个默认成员函数,这6个默认成员函数如果用户没有实现,则会由编译器默认生成。

        6个默认成员函数包括:负责初始化工作的构造函数;负责清理工作的析构函数;在用同类对象对创建对象进行初始化时用到的拷贝构造;用于对象之间赋值的赋值操作符重载;还有两个很少自己实现的取地址操作符重载const修饰的取地址操作符重载

2.1 构造函数

        构造函数是一个特殊的成员函数,帮助我们对新创建的对象进行初始化。

class Date
{
private:int _year;int _month;int _day;
public:Date(int year = 2000, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//void Init(int year = 2000, int month = 1, int day = 1)//{//	_year = year;//	_month = month;//	_day = day;//}void Print(){cout << _year << '-' << _month << '-' << _day << endl;}
};
int main()
{Date d1;Date d2(2024, 2, 26);//d1.Init(2024, 2, 26);d1.Print();d2.Print();
}

构造函数说明

        对于构造函数,我们需要对其进行一些说明与强调:

①构造函数属于默认成员函数,当发现我们没有写时编译器会默认生成一个,当我们写了编译器就不再会生成

②我们写构造函数时需要注意:构造函数的函数名应于类名相同;构造函数没有返回值返回类型的void不写;构造函数可以重载

class Date
{
private:int _year;int _month;int _day;
public:Date(){}Date(int year, int month, int day){_year = year;_month = month;_day = day;}
};
int main()
{Date d1();  //error 调用无参构造函数时,如果跟上()就成了函数声明Date d1;Date d2(2024, 2, 26);
}

③构造函数会在创建对象的时候自动调用,且在该对象的生命周期内仅调用这一次。

class Date
{
private:int _year;int _month;int _day;
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << '-' << _month << '-' << _day << endl;}
};
int main()
{Date d1(2024, 2, 26);d1.Print();	//2024-2-26
}

④构造函数的任务不是开辟空间创造对象,而是对创建的对象进行初始化。

⑤编译器默认生成的构造函数,对内置类型(int、char等)不会做处理对自定义类型(类等)会调用该自定义类型的构造函数

class Time
{
private:int _hour;int _minute;int _second;
public:Time(){cout << "Time()" << endl;}
};
class Date
{
private:int _year;int _month;int _day;Time _t;
public:void Print(){cout << _year << '-' << _month << '-' << _day << endl;}
};
int main()
{Date d1;d1.Print();//output//Time()//随机值
}

⑥C++11规定:内置类型可以在类中给默认值。

class Date
{
private://在类中给默认值int _year = 2000;int _month = 1;int _day = 1;
public:void Print(){cout << _year << '-' << _month << '-' << _day << endl;}
};
int main()
{Date d1;d1.Print();	//2000-1-1
}

2.2 析构函数

        构造函数是在对象创建后默认调用,用于初始化的。析构函数则是在对象的生命周期结束时调用自动析构函数,用来销毁对象,完成对象中资源的清理工作。

析构函数说明

①析构函数也属于默认成员函数,所以当我们没有写时编译器会默认生成一个,当我们写了编译器就不再会生成

②注意析构函数形式:析构函数的函数名为 ~类名 ;析构函数没有返回值和返回类型;析构函数不可以重载

class Stack
{
private:int* _arr;int _capacity;int _top;
public:Stack(int capacity = 4){_arr = (int*)malloc(sizeof(int) * capacity);if (_arr == nullptr){perror("malloc fail");return;}_capacity = capacity;_top = 0;}void Push(int x){//CheckCapacity();_arr[_top++] = x;}~Stack(){free(_arr);_arr = nullptr;_capacity = 0;_top = 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;//output://~Time()
}

 ⑤如果类中成员变量没有申请资源时,析构函数可以不需要写,使用默认即可。但是如果存在资源申请(如malloc),需要自己写出对应的析构函数来正确释放空间。

⑥在此处我们总结一下对于局部、全局、静态对象调用构造和析构的顺序问题:

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

         我们创建了一批对象,再令main函数正常结束,观察输出,可以总结出构造函数和析构函数调用的顺序:

构造函数:按照代码执行顺序进行创建。①顺序执行全局(包括全局静态)对象的创建;②进入main函数,顺序执行;③遇到函数,进入后顺序执行。

析构函数:按照声明周期结束时间进行调用。局部对象(后定义先析构)->全局和静态对象(后定义先析构)。

2.3 拷贝构造函数

        拷贝构造函数是一种特殊的构造函数,也是在初始化的时候发挥作用。拷贝构造函数只有一个由const修饰的本类类型对象的引用作为参数,在用已存在的类类型对象创建新对象时自动调用。

拷贝构造函数说明

①拷贝构造函数实际上是构造函数的一个重载形式。但是既然能成为默认成员函数,就意味着当我们没有写时编译器会默认生成一个,当我们写了编译器就不再会生成

class Date
{
private:int _year;int _month;int _day;
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << ' ' << _month << ' ' << _day << endl;}
};
int main()
{Date d1(2024, 2, 27);d1.Print();Date d2(d1);d2.Print();
}

②注意拷贝构造函数形式:拷贝构造函数是构造函数的重载形式,所以除了参数和构造函数一样。拷贝构造函数的参数只有一个,是const修饰的本类类型对象的引用。但是我们了解了this指针的存在,所以要注意分清调用的形式,实参应该是已经拷贝的“原件”。

class Date
{
private:int _year;int _month;int _day;
public:
//构造函数Date(int year, int month, int day){_year = year;_month = month;_day = day;}
//拷贝构造函数Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}void Print(){cout << _year << ' ' << _month << ' ' << _day << endl;}
};
int main()
{Date d1(2024,2,27);d1.Print();Date d2(d1);d2.Print();
}

③对于参数的说明:拷贝构造的一个参数必须是类类型对象的引用,如果不传引用而传值则会导致无穷递归调用引发报错。

void func(Date d)
{}
int main()
{Date d1(2024,2,27);func(d1);
}

        对于以上代码,在func函数使用了值传递,实际上就是将d1的值传给参d。类似于C语言中的结构体传值调用,C++的自定义类型值传递时都需要调用拷贝构造,在执行到这里的时候编译器将d1的值拷贝到d中,所以这时就需要调用Date类的拷贝构造函数。

	Date(const Date d) //error{_year = d._year;_month = d._month;_day = d._day;}

        那么如果在实现拷贝构造函数的时候传值,那么在调用拷贝构造函数的时候,参数为了拿到值会再去调用拷贝构造,这次的拷贝构造仍然是值传递,为了获取参数的值会去调用下一次拷贝构造,因而产生无穷递归。

④编译器默认生成的拷贝构造函数,对内置类型是对内存存储中字节完成拷贝,这种拷贝称为浅拷贝,或值拷贝。对于自定义类型则是调用它的拷贝构造函数

class Time
{
public:Time(){_hour = 1;}Time(const Time& t){_hour = t._hour;cout << "Time::Time(const Time&)" << endl;}
private:int _hour;
};
class Date
{
private:// 基本类型(内置类型)int _year = 2000;// 自定义类型Time _t;
};
int main()
{Date d1;Date d2(d1);return 0;//oupput://Time::Time(const Time&)
}

⑤深拷贝和浅拷贝:浅拷贝仅仅是对字节进行逐一拷贝,这种拷贝方式在面对正常情况并无问题,但是一旦遇到申请资源的成员,浅拷贝便会产生问题。

class Stack
{
private:int* _arr;int _capacity;int _top;
public:Stack(int capacity = 4){_arr = (int*)malloc(sizeof(int) * capacity);if (_arr == nullptr){perror("malloc fail");return;}_capacity = capacity;_top = 0;}
};
int main()
{Stack st1;Stack st2(st1);return 0;
}

        对于上述栈这个类,如果仅仅是浅拷贝的方式,那么st2的_arr成员的值就会和st1的_arr值相同。但是我们知道这个_arr成员指向的是对上开辟的空间,如果我们的两个栈st1和st2的_arr成员相同,那就意味着是二者共用一块空间,这就出现了问题。

        对于这种申请了资源的情况,我们就需要深拷贝,即对对象下所管理的深层空间也进行拷贝,此时就需要自己实现拷贝构造函数了。

class Stack
{
private:int* _arr;int _capacity;int _top;
public:Stack(int capacity = 4){_arr = (int*)malloc(sizeof(int) * capacity);if (_arr == nullptr){perror("malloc fail");return;}_capacity = capacity;_top = 0;}Stack(const Stack& st){_arr = (int*)malloc(sizeof(int) * st._capacity);if (_arr == nullptr){perror("malloc fail");return;}memcpy(_arr, st._arr, sizeof(int) * st._capacity);_capacity = st._capacity;_top = st._top;}
};
int main()
{Stack st1;Stack st2(st1);return 0;
}

⑥拷贝构造函数自动调用的场景:a.用存在的对象初始化新对象;b.函数参数类型为类类型对象;c.函数的返回值类型为类类型对象。

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

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

相关文章

python dictionary 字典

Python 字典 字典是另一种可变容器模型&#xff0c;且可存储任意类型对象。 字典的每个键值 key>value 对用冒号 : 分割&#xff0c;每个对之间用逗号(,)分割&#xff0c;整个字典包括在花括号 {} 中 ,格式如下 d {key1 : value1, key2 : value2, key3 : value3 }dict 作…

RocketMQ入坑指南(五):SpringBoot集成RocketMQ和具体使用方式

前言 经过前面几部分的教程&#xff0c;大家应该已经对RocketMQ有了一个全面的认识&#xff0c;建议仔细阅读前几章的内容&#xff0c;可以更好的理解这次的内容&#xff0c;接下来&#xff0c;我们通过代码来演示一下SpringBoot如何集成并使用RocketMQ发送消息 一、SpringBo…

Linux笔记--用户与用户组

Linux系统是一个多用户多任务的操作系统&#xff0c;任何一个要使用系统资源的用户&#xff0c;都必须首先向系统管理员(root)申请一个账号&#xff0c;然后以这个账号的身份进入系统。 用户的账号一方面可以帮助系统管理员对使用系统的用户进行跟踪&#xff0c;并控制他们对系…

Unity中URP下实现水体(水面反射)

文章目录 前言一、原理1、法一&#xff1a;使用立方体纹理 CubeMap&#xff0c;作为反射纹理使用2、法二&#xff1a;使用反射探针生成环境反射图&#xff0c;所谓反射的采样纹理 二、实现水面反射1、定义和申明CubeMap2、反射向量需要什么3、计算 N ⃗ \vec{N} N 4、计算 V ⃗…

【C++私房菜】序列式容器的迭代器失效问题

目录 一、list的迭代器失效 二、vector的迭代器失效 1、空间缩小操作 2、空间扩大操作 三、总结 在C中&#xff0c;当对容器进行插入或删除操作时&#xff0c;可能会导致迭代器失效的问题。所谓迭代器失效指的是&#xff0c;原先指向容器中某个元素的迭代器&#xff0c;在…

STM32_DS18B20_1_芯片简介及初始化配置

DS18B20介绍 DS18B20数字温度计提供9位到12位摄氏度的温度测量&#xff0c;并具有非易失性&#xff0c;用户可编程的上下触发点的报警功能。DS18B20通过1线总线进行通信&#xff0c;根据定义&#xff0c;该总线只需要一条数据线&#xff0c;即可与中央微处理器进行通信…

5G双域快网

目录 一、业务场景 二、三类技术方案 2.1、专用DNN方案 2.2、ULCL方案&#xff1a;通用/专用DNNULCL分流 2.3、 多DNN方案-定制终端无感分流方案 漫游场景 一、业务场景 初期双域专网业务可划分为三类业务场景&#xff0c;学校、政务、文旅等行业均已提出公/专网融合访问需…

每日五道java面试题之spring篇(九)

目录&#xff1a; 第一题. 说一下Spring的事务传播行为第二题. 说一下 spring 的事务隔离&#xff1f;第三题. Spring AOP and AspectJ AOP 有什么区别&#xff1f;AOP 有哪些实现方式&#xff1f;第四题. JDK动态代理和CGLIB动态代理的区别第五题. 解释一下Spring AOP里面的几…

nginx实现http反向代理及负载均衡

目录 一、代理概述 1、代理概念 1.1 正向代理&#xff08;Forward Proxy&#xff09; 1.2 反向代理&#xff08;Reverse Proxy&#xff09; 1.3 正向代理与反向代理的区别 2、同构代理与异构代理 2.1 同构代理 2.2 异构代理 2.3 同构代理与异构代理的区别 二、四层代…

VL817-Q7 USB3.0 HUB芯片 适用于扩展坞 工控机 显示器

VL817-Q7 USB3.1 GEN1 HUB芯片 VL817-Q7 USB3.1 GEN1 HUB芯片 VIA Lab的VL817是一款现代USB 3.1 Gen 1集线器控制器&#xff0c;具有优化的成本结构和完全符合USB标准3.1 Gen 1规范&#xff0c;包括ecn和2017年1月的合规性测试更新。VL817提供双端口和双端口4端口配置&…

Linux NFC 子系统剖析

1.总览 linux源码中NFC在net/nfc下&#xff0c;文件结构如下图&#xff1a; hci&#xff1a;Host Controller Interface 主要是针对NFC的主机-控制器接口协议 nci&#xff1a;NFC Controller Interface 主要是NFC的控制器接口协议&#xff0c;用于NFCC(NFC Controller)和DH(…

【Go语言】Go语言中的切片

Go语言中的切片 1.切片的定义 Go语言中&#xff0c;切片是一个新的数据类型数据类型&#xff0c;与数组最大的区别在于&#xff0c;切片的类型中只有数据元素的类型&#xff0c;而没有长度&#xff1a; var slice []string []string{"a", "b", "c…

GCC的符号可见性: 解决Linux多个库同名符号冲突问题以及引用不同版本库的问题

目录 1 -fvisibilitydefault|internal|hidden|protected 1.1 __attribute__((visibility("default"))) 与 CXXg -fvisibilityhidden 的作用 1.2 __attribute__((visibility("hidden"))) 与 CXXg -fvisibilitydefault的作用 2 我的问题 2.1 解决措…

雾锁王国服务器怎么建?雾锁王国服务器搭建方法

雾锁王国Enshrouded服务器搭建怎么搭建&#xff1f;非常简单&#xff0c;阿里云计算巢雾锁王国程序&#xff0c;可以一键搭建雾锁王国多人联机服务器&#xff0c;腾讯云是基于雾锁王国镜像系统&#xff0c;阿里云服务网aliyunfuwuqi.com汇总雾锁王国服务器搭建&#xff0c;超简…

kafka三节点集群平滑升级过程指导

一、前言 Apache Kafka作为常用的开源分布式流媒体平台&#xff0c;可以实时发布、订阅、存储和处理数据流,多用于作为消息队列获取实时数据&#xff0c;构建对数据流的变化进行实时反应的应用程序&#xff0c;已被数千家公司用于高性能数据管道、流分析、数据集成和任务关键型…

DolphinScheduler——工作流实例的生命周期

目录 一、DolphinScheduler架构原理 1.1 系统架构图 1.2 DolphinScheduler核心概念 1.2 创建工作流 1.2.1 如何触发一个工作流实例 1.2.2 任务调度链路监控 1.2.3 Workflow-DAG解析 DAG解析 Dispatch分发流程 Master和Worker的交互过程 1.3 任务运行状态 该篇文章主…

就业班 2401--2.28 Linux Day7--存储管理1

一 .存储管理 主要知识点: 基本分区、逻辑卷LVM、EXT3/4/XFS文件系统、RAID 初识硬盘 机械 HDD 固态 SSD SSD的优势 SSD采用电子存储介质进行数据存储和读取的一种技术&#xff0c;拥有极高的存储性能&#xff0c;被认为是存储技术发展的未来新星。 与传统硬盘相比&#…

Codeforces Round 929 (Div. 3)(A,B,C,D,E,F,G)

这场没考什么算法&#xff0c;比较水&#xff0c;难度也不是很高。比赛链接 硬要说的话E有个 前缀和 加 二分&#xff0c;F是数学BFS&#xff0c;G是个构造 A. Turtle Puzzle: Rearrange and Negate 题意&#xff1a; 给你一个由 n n n 个整数组成的数组 a a a 。您必须对…

Rocky Linux 运维工具yum

一、yum的简介 ​​yum​是用于在基于RPM包管理系统的包管理工具。用户可以通过 ​yum​来搜索、安装、更新和删除软件包&#xff0c;自动处理依赖关系&#xff0c;方便快捷地管理系统上的软件。 二、yum的参数说明 1、install 用于在系统的上安装一个或多个软件包 2、seach 用…

golang使用gorm操作mysql1

1.mysql连接配置 package daoimport ("fmt""gorm.io/driver/mysql""gorm.io/gorm""gorm.io/gorm/logger" )var DB *gorm.DB// 连接数据库&#xff0c;启动服务的时候&#xff0c;init方法就会执行 func init() {username : "roo…