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

【C++】对象特性:无参有参构造函数,拷贝构造函数,析构函数

目录

      • 对象的初始化和清理
        • 1.1 构造函数和析构函数
        • 1.2 构造函数的分类及调用
        • 1.3 拷贝构造函数调用时机
        • 1.4 构造函数调用规则
        • 1.5 深拷贝与浅拷贝

对象的初始化和清理

  • 生活中我们买的电子产品都基本会有出厂设置,在某一天我们不用时候也会删除一些自己信息数据保证安全。

  • C++中的面向对象来源于生活,每个对象也都会有初始设置以及 对象销毁前的清理数据的设置。

1.1 构造函数和析构函数

对象的初始化和清理也是两个非常重要的安全问题。

一个对象或者变量没有初始状态,对其使用后果是未知。

同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题。

c++利用了构造函数析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。

对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会提供

编译器提供的构造函数和析构函数是空实现。

  • 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。

  • 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。

构造函数语法:类名(){}

  1. 构造函数,没有返回值也不写void

  2. 函数名称与类名相同

  3. 构造函数可以有参数,因此可以发生重载

  4. 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次

析构函数语法: ~类名(){}

  1. 析构函数,没有返回值也不写void

  2. 函数名称与类名相同,在名称前加上符号 ~

  3. 析构函数不可以有参数,因此不可以发生重载

  4. 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次

示例代码

#include<iostream>
using namespace std;
//对象的初始化和清理
//1、构造函数 进行初始化操作
//2、析构函数 进行清理的操作
class Person
{
public://1.1、构造函数//没有返回值 不用void//函数名 与类名称相同//构造函数可以有参数,可以发生重载//创建对象的时候,构造函数会自动调用,且只调用一次Person(){cout << "Person 构造函数的调用" << endl;}//1.2、析构函数//没有返回值 不写void//函数名和类名相同 在名称前加~//析构函数不可以有参数,不可以发生重载//对象销毁前 会自动调用析构函数 而且只会调用一次~Person(){cout << "Person 析构函数的调用" << endl;}
};void test01()
{Person P;
}
int main()
{test01();system("pause");return 0;
}

运行结果

Person 构造函数的调用
Person 析构函数的调用
1.2 构造函数的分类及调用

两种分类方式:

1)按参数分为: 有参构造和无参构造

2)按类型分为: 普通构造和拷贝构造

三种调用方式:

1)括号法

2)显示法

3)隐式转换法

构造函数的分类

#include<iostream>
using namespace std;//1构造函数的分类及调用
//分类
//按照参数分类 无参构造(默认构造) 和 有参构造
//按照类型分类 普通构造 和 拷贝构造
class Person
{
public:int age;//构造函数Person(){cout << "Person 无参构造函数的调用" << endl;}//有参构造函数Person(int a){age = a;cout << "Person 有参构造函数的调用" << endl;}//拷贝构造函数Person(const Person &p){//将传入的人身上的所有属性,拷贝在我的身上 age = p.age;cout << "Person 拷贝构造函数的调用" << endl;}//析构函数~Person(){cout << "Person 析构函数的调用" << endl;}
};

构造函数的调用

1)构造函数的调用:括号法

void test01()
{//1、括号法Person p1; //默认构造函数的调用Person p2(10); //有参构造函数的调用Person p3(p2); //拷贝构造函数的调用//注意事项1//调用默认构造函数时候,不用加()//因为下面这行代码,编译器会认为是一个函数的声明//Person p1();cout << "p2的年龄为:" << p2.age << endl;cout << "p3的年龄为:" << p3.age << endl;
}int main()
{test01();system("pause");return 0;
}

运行结果

Person 无参构造函数的调用
Person 有参构造函数的调用
Person 拷贝构造函数的调用
p2的年龄为:10
p3的年龄为:10
Person 析构函数的调用
Person 析构函数的调用
Person 析构函数的调用

2)构造函数的调用:显示法

void test01()
{//2、显示法Person p1;Person p2 = Person(10); //有参构造Person p3 = Person(p2); //拷贝构造//Person(10); //匿名对象 特点:当前行执行结束后,系统会立即回收掉匿名对象//注意事项2//不要利用拷贝构造函数 初始化匿名对象 编译器会认为 Person(p3); 等价于 Person p3; 重定义,报错。//Person(p3);cout << "p2的年龄为:" << p2.age << endl;cout << "p3的年龄为:" << p3.age << endl;
}
int main()
{test01();system("pause");return 0;
}

运行结果

Person 无参构造函数的调用
Person 有参构造函数的调用
Person 拷贝构造函数的调用
p2的年龄为:10
p3的年龄为:10
Person 析构函数的调用
Person 析构函数的调用
Person 析构函数的调用

3)构造函数的调用:

void test01()
{//3、隐式转换法Person p4 = 10; //相当于 写了 Person(10); Person p5 = p4; //拷贝构造cout << "p4的年龄:" << p4.age << endl;cout << "p5的年龄:" << p5.age << endl;
}
int main()
{test01();system("pause");return 0;
}

运行结果

Person 有参构造函数的调用
Person 拷贝构造函数的调用
p4的年龄:10
p5的年龄:10
Person 析构函数的调用
Person 析构函数的调用
1.3 拷贝构造函数调用时机

C++中拷贝构造函数调用时机通常有三种情况。

  • 使用一个已经创建完毕的对象来初始化一个新对象。

  • 值传递的方式给函数参数传值。

  • 以值方式返回局部对象。

示例代码

#include<iostream>
using namespace std;//拷贝构造函数调用时机//1、使用一个已经创建完毕的对象来初始化一个新对象//2、值传递的方式给函数参数传值//3、值方式返回局部对象
class Person
{
public:int m_Age;Person(){cout << "Person默认构造函数的调用" << endl;}Person(int age){m_Age = age;cout << "Person有参构造函数的调用" << endl;}Person(const Person& p){m_Age = p.m_Age;cout << "Person拷贝构造函数的调用" << endl;}~Person(){cout << "Person默认析构函数的调用" << endl;}
};

1、使用一个已经创建完毕的对象来初始化一个新对象。

//1、使用一个已经创建完毕的对象来初始化一个新对象
void test01()
{Person  p1(20);Person  p2(p1);cout << "p2的年龄为:" << p2.m_Age << endl;
}
int main()
{test01();system("pause");return 0;
}

运行结果

Person有参构造函数的调用
Person拷贝构造函数的调用
p2的年龄为:20
Person默认析构函数的调用
Person默认析构函数的调用

2、值传递的方式给函数参数传值

//2、值传递的方式给函数参数传值
//相当于Person p1 = p;
void doWork(Person p1)
{}
void test02()
{Person p;doWork(p);
}

运行结果

Person默认构造函数的调用
Person拷贝构造函数的调用
Person默认析构函数的调用
Person默认析构函数的调用

3、值方式返回局部对象

//3、值方式返回局部对象
Person doWork03()
{Person p1;cout << "p1的地址为:" << (int*)&p1 << endl;return p1;
}
void test03()
{Person p = doWork03();cout << "p的地址为:" << (int*)&p << endl;
}

运行结果

Person默认构造函数的调用
010FF894
Person拷贝构造函数的调用
Person默认析构函数的调用
010FF98C
Person默认析构函数的调用

运行结果(VS2022)编译器会优化

Person默认构造函数的调用
p1的地址为:000000B083EFF5D4
p的地址为:000000B083EFF5D4
Person默认析构函数的调用
1.4 构造函数调用规则

默认情况下,c++编译器至少给一个类添加3个函数。

1.默认构造函数(无参,函数体为空)。

2.默认析构函数(无参,函数体为空)。

3.默认拷贝构造函数,对属性进行值拷贝。

构造函数调用规则如下:

  • 如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造。

  • 如果用户定义拷贝构造函数,c++不会再提供其他构造函数。

示例代码

#include<iostream>
using namespace std;
//构造函数的调用规则
//1、创建一个类,C++编译器会给每个类都添加至少三个函数
//默认构造(空实现)
//析构函数(空实现)
//拷贝构造(值拷贝)//2、
// 如果我们写了有参构造函数,编译器就不再提供默认构造,依然提供拷贝构造
// 如果我们写了拷贝构造函数,编译器就不再提供其他普通构造函数了class Person
{
public:int m_Age;Person(){cout << "Person默认构造函数的调用" << endl;}Person(int age){m_Age = age;cout << "Person有参构造函数的调用" << endl;}/*Person(const Person& p){m_Age = p.m_Age;cout << "Person拷贝构造函数的调用" << endl;}*/~Person(){cout << "Person默认析构函数的调用" << endl;}
};void test01()
{Person p;p.m_Age = 18;Person p2(p);cout << "p2的年龄为:" << p2.m_Age << endl;
}int main()
{test01();system("pause");return 0;
}

运行结果

Person默认构造函数的调用
p2的年龄为:18
Person默认析构函数的调用
Person默认析构函数的调用

可以看到我们定义了有参构造函数,而我们没有定义拷贝构造函数,但是编译器为我们提供了拷贝构造函数,将p.m_Age = 18值拷贝给了p2。

1.5 深拷贝与浅拷贝

深浅拷贝是面试经典问题,也是常见的一个坑。

浅拷贝:简单的赋值拷贝操作。

深拷贝:在堆区重新申请空间,进行拷贝操作。

示例代码

#include<iostream>
using namespace std;
//深拷贝与浅拷贝class Person
{
public:int m_Age;int *m_Height; //身高
public:Person() {cout << "Person的默认构造函数调用" << endl;}Person(int age, int height){cout << "Person的有参构造函数调用" << endl;m_Age = age;m_Height = new int(height);}//自己实现拷贝构造函数,解决浅拷贝带来的问题Person(const Person& p){cout << "Person 拷贝构造函数调用" << endl;m_Age = p.m_Age;//m_Height = p.m_Height; 编译器默认实现就是这行代码//深拷贝操作m_Height = new int(*p.m_Height);}~Person(){//析构代码,将堆区开辟数据做释放操作if (m_Height != NULL){delete m_Height;m_Height = NULL;}cout << "Person的默认析构函数调用" << endl; }
};void test01()
{Person p1(18,160);Person p2(p1);cout << "p1的年龄为:"  << p1.m_Age << " 身高为:" << *p1.m_Height << endl;cout << "p2的年龄为:" << p2.m_Age << " 身高为:" << *p2.m_Height << endl;
}int main()
{test01();system("pause");return 0;
}

运行结果

Person的有参构造函数调用
Person 拷贝构造函数调用
p1的年龄为:18 身高为:160
p2的年龄为:18 身高为:160
Person的默认析构函数调用
Person的默认析构函数调用

假如,我们没有自行设计拷贝构造函数,那么编译器会默认为浅拷贝,也就是说首先p2析构,内存释放,然后p1析构,内存释放,然而此时对应内存已经释放过了,无法释放第二次,所以会报错。

浅拷贝的意思就是 p1.m_Height在堆区开辟内存,对应的地址为0x0011,而拷贝给p2.m_Height,它对应的地址也是0x0011。

而深拷贝也就是说, p1.m_Height在堆区开辟内存,对应的地址为0x0011,而拷贝给p2.m_Height,p2.m_Height开辟了一个新地址,0x0022,但是0x0011和0x0022的内容都是160。

总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题。

相关文章:

【C++】对象特性:无参有参构造函数,拷贝构造函数,析构函数

目录 对象的初始化和清理1.1 构造函数和析构函数1.2 构造函数的分类及调用1.3 拷贝构造函数调用时机1.4 构造函数调用规则1.5 深拷贝与浅拷贝 对象的初始化和清理 生活中我们买的电子产品都基本会有出厂设置&#xff0c;在某一天我们不用时候也会删除一些自己信息数据保证安全。…...

【算法与数据结构】1005、LeetCode K 次取反后最大化的数组和

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;本题允许某个下标的数字多次翻转&#xff0c;因此思路比较简单。首先&#xff0c;我们要求最大和&…...

作业--day34

使用select完成TCP并发服务器和客户端 server.c #include <myhead.h>#define PORT 8888 #define IP "192.168.125.137"int main(int argc, const char *argv[]) {int sfd socket(AF_INET, SOCK_STREAM, 0);if(sfd -1){perror("socket error");re…...

车辆违规开启远光灯检测系统:融合YOLO-MS改进YOLOv8

1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 研究背景与意义 随着社会的不断发展和交通工具的普及&#xff0c;车辆违规行为成为了一个严重的问题。其中&#xff0c;车辆违规开启远光灯是一种常见的违规行为&#xff0c;给其…...

爬虫工作量由小到大的思维转变---<第十三章 Scrapy之pipelines分离的思考>

前言: 收到留言: "我的爬取的数据处理有点大,scrapy抓网页挺快,处理数据慢了!" -----针对这位粉丝留言,我只想说:你那培训班老师可能给你漏了课程! 大概你们上课讲的案例属于demo,他教了你一些基本操作,但他没有对相关业务对你讲透! 你研究一下pipelines,或者看我现…...

【Unity】运行时创建曲线(贝塞尔的运用)

[Unity]运行时创建线&#xff08;贝塞尔的运用&#xff09; 1. 实现的目标 在运行状态下创建一条可以使用贝塞尔方法实时编辑的网格曲线。 2. 原理介绍 2.1 曲线的创建 unity建立网格曲线可以参考Unity程序化网格体的实现方法。主要分为顶点&#xff0c;三角面&#xff0c…...

基于DSP的IIR数字滤波器(论文+源码)

1.系统设计 在本次基于DSP的IIR数字低通滤波计中&#xff0c;拟以TMS320F28335来作为系统的主控制器&#xff0c;通过ADC0832模数转换芯片来对输入信号进行采集&#xff1b;通过TLC5615来将低通滤波后的信号进行输出&#xff1b;同时结合MATLAB仿真软件&#xff0c;对设计的II…...

Django(一)

1.web框架底层 1.1 网络通信 注意&#xff1a;局域网 个人一般写程序&#xff0c;想要让别人访问&#xff1a;阿里云、腾讯云。 去云平台租服务器&#xff08;含公网IP&#xff09;程序放在云服务器 先以局域网为例 我的电脑【服务端】 import socket# 1.监听本机的IP和…...

微信小程序如何利用createIntersectionObserver实现图片懒加载

微信小程序如何利用createIntersectionObserver实现图片懒加载 节点布局相交状态 API 可用于监听两个或多个组件节点在布局位置上的相交状态。这一组API常常可以用于推断某些节点是否可以被用户看见、有多大比例可以被用户看见。 节点布局相交状态 API中有一个 wx.createInter…...

七:爬虫-数据解析之正则表达式

七&#xff1a;正则表达式概述 正则表达式&#xff0c;又称规则表达式,&#xff08;Regular Expression&#xff0c;在代码中常简写为regex、regexp或RE&#xff09;&#xff0c;是一种文本模式&#xff0c;包括普通字符&#xff08;例如&#xff0c;a 到 z 之间的字母&#xf…...

云原生之深入解析亿级流量架构之服务限流思路与方法

一、限流思路 ① 熔断 系统在设计之初就把熔断措施考虑进去&#xff0c;当系统出现问题时&#xff0c;如果短时间内无法修复&#xff0c;系统要自动做出判断&#xff0c;开启熔断开关&#xff0c;拒绝流量访问&#xff0c;避免大流量对后端的过载请求。系统也应该能够动态监测…...

【Python炫酷系列】祝考研的友友们金榜题名吖(完整代码)

文章目录 环境需求完整代码详细分析系列文章环境需求 python3.11.4及以上版本PyCharm Community Edition 2023.2.5pyinstaller6.2.0(可选,这个库用于打包,使程序没有python环境也可以运行,如果想发给好朋友的话需要这个库哦~)【注】 python环境搭建请见:https://want595.…...

KL散度、CrossEntropy详解

文章目录 0. 概述1. 信息量1.1 定义1.2 性质1.3 例子2. 熵 Entropy2.1 定义2.2 公式2.3 例子3. 交叉熵 Cross Entropy3.1 定义3.2 公式3.3 例子4. KL 散度(相对熵)4.1 公式...

【算法】红黑树

一、红黑树介绍 红黑树是一种自平衡二叉查找树&#xff0c;是在计算机科学中用到的一种数据结构&#xff0c;典型的用途是实现关联数组。 红黑树是在1972年由Rudolf Bayer发明的&#xff0c;当时被称为平衡二叉B树&#xff08;symmetric binary B-trees&#xff09;。后来&am…...

2023楚慧杯 WEB方向 部分:(

1、eaaeval 查看源码能看见账号&#xff1a;username169&#xff0c;密码&#xff1a;password196提交这个用户密码可以跳转到页面/dhwiaoubfeuobgeobg.php 通过dirsearch目录爆破可以得到www.zip <?php class Flag{public $a;public $b;public function __construct(){…...

STM32 CAN多节点组网项目实操 挖坑与填坑记录2

系列文章&#xff0c;持续探索CAN多节点通讯&#xff0c; 上一篇文章链接&#xff1a; STM32 CAN多节点组网项目实操 挖坑与填坑记录-CSDN博客文章浏览阅读120次。CAN线性组网项目开发过程中遇到的数据丢包问题&#xff0c;并尝试解决的记录和推测分析。开发了一个多节点线性…...

Flink 数据类型 TypeInformation信息

Flink流应用程序处理的是以数据对象表示的事件流。所以在Flink内部&#xff0c;我么需要能够处理这些对象。它们需要被序列化和反序列化&#xff0c;以便通过网络传送它们&#xff1b;或者从状态后端、检查点和保存点读取它们。为了有效地做到这一点&#xff0c;Flink需要明确知…...

基于python的leetcode算法介绍之递归

文章目录 零 算法介绍一 简单示例 辗转相除法Leetcode例题与思路[509. 斐波那契数](https://leetcode.cn/problems/fibonacci-number/)解题思路&#xff1a;题解&#xff1a; [206. 反转链表](https://leetcode.cn/problems/reverse-linked-list/)解题思路&#xff1a;题解&…...

2023年度佳作:AIGC、AGI、GhatGPT、人工智能大语言模型的崛起与挑战

目录 前言 01 《ChatGPT 驱动软件开发》 内容简介 02 《ChatGPT原理与实战》 内容简介 03 《神经网络与深度学习》 04 《AIGC重塑教育》 内容简介 05 《通用人工智能》 目  录 前言 2023年是人工智能大语言模型大爆发的一年&#xff0c;一些概念和英文缩写也在这一…...

Axure的交互以及情形的介绍

一. 交互 1.1 交互概述 通俗来讲就是&#xff0c;谁用了什么方法做了什么事情&#xff0c;主体"谁"对应的就是axure中的元件&#xff0c;"什么方法"对应的就是交互事件&#xff0c;比如单击事件、双击事件&#xff0c;"什么事情"对应的就是交互…...

ubuntu搭建nfs服务centos挂载访问

在Ubuntu上设置NFS服务器 在Ubuntu上&#xff0c;你可以使用apt包管理器来安装NFS服务器。打开终端并运行&#xff1a; sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享&#xff0c;例如/shared&#xff1a; sudo mkdir /shared sud…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制&#xff08;1&#xff09;三次握手①握手过程②对握手过程的理解 &#xff08;2&#xff09;四次挥手&#xff08;3&#xff09;握手和挥手的触发&#xff08;4&#xff09;状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

【Java_EE】Spring MVC

目录 Spring Web MVC ​编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 ​编辑参数重命名 RequestParam ​编辑​编辑传递集合 RequestParam 传递JSON数据 ​编辑RequestBody ​…...

大数据学习(132)-HIve数据分析

​​​​&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

Mac下Android Studio扫描根目录卡死问题记录

环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中&#xff0c;提示一个依赖外部头文件的cpp源文件需要同步&#xff0c;点…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...