类的六个构造函数相关干货

news2025/1/16 5:35:42

构造函数

特点

1.名字与类名相同
2.无返回值
3.对象实例化的时候编译器自动调用这个函数
4.构造函数可以重载(无参构造函数,拷贝构造等)
5.如果类中没有显式定义构造函数(深拷贝),则编译器会自动生成一个默认构造函数(浅拷贝/值拷贝),若显式定义了编译器就不再生成

class Date
{
public:
	Date()//无参构造函数
	{}
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
public:
	void test1()
	{
		Date d1;//无参
		Date d2(2024, 4, 14);//带参
	}
};

6.c++把类型分为内置类型和自定义类型,对于内置类型来说,编译器自动生成的默认构造函数是不会将内置类型初始化的(为了弥补这个缺陷,c++11中的内置成员变量在声明的时候是可以给默认值的),所以如果下面这个代码我们没有主动去设置构造函数,里面的三个成员变量就都是随机值,若他是自定义类型,编译器就会去调用这个类型的默认构造函数

#include<iostream>
using namespace std;
class Day
{
public:
	Day()
	{
		_day = 0;
		cout << "Day()" << endl;
	}
private:
	int _day;
};
class Date
{
private:
	int _year=2024;
	int _month=4;
	Day _day;
};
int main()
{
	Date d1;
	return 0;
}

7.无参的构造函数,全缺省的构造函数和编译器自动生成的构造函数都可以被称为默认构造函数

初始化列表

来由

调用构造函数赋值时,我们会给变量一个初始值,但是这不能称为变量的初始化,只能称为赋值,因为初始化只能初始化一次,而构造函数中可以对变量多次赋值,于是就产生了初始化列表。

用法

以一个冒号为开始,每个成员变量后面跟着一个用括号隔开的值或者表达式

class vientiane
{
public:
	vientiane(int year,int month,int day)
	:_year(year)
	,_month(month)
	,_day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
}

注意:每个成员变量在初始化列表中只能初始化一次,也就是只能出现一次,当类中成员变量包含引用成员变量,const类型成员变量,没有默认构造函数的类类型成员变量时,都要放在初始化列表初始化,我们也尽量使用初始化列表初始化,因为不管你用不用初始化列表,程序都会先走初始化列表再执行构造函数里面的内容的,成员变量的声明顺序就是初始化列表的初始化顺序,与初始化列表里的顺序是无关的,所以最好不要让初始化的成员互相初始化,很容易得到随机值

explicit关键字

我们偶尔会看到如下代码

class vientiane
{
public:
	vientiane(int year,int month=1,int day=1)
	:_year(year)
	,_month(month)
	,_day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
}
int main()
{
	vientiane d1(2024);
	vientiane d2=2024;
	return 0;
}

也许你会疑惑,d2是一个自定义类型,而2024是一个整形,为什么可以这样直接赋值呢?其实,c++里有一种隐式类型转换,比如说你现在有一个整形变量a=1,你想让他赋值给double类型变量b,那么在赋值过程中会产生一个double类型的临时变量,这个double类型的临时变量被赋值成1.0,然后再用这个临时变量去给b赋值,上图那段代码也一样,“2024”会先调用构造函数构造一个vientiane类型的变量,然后再用拷贝构造去初始化d2,如果你不想要有这样的类型转换,你可以在构造函数前加一个explicit关键字

class vientiane
{
public:
	explicit vientiane(int year,int month=1,int day=1)
	:_year(year)
	,_month(month)
	,_day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
}
int main()
{
	vientiane d1(2024);
	vientiane d2=2024;
	return 0;
}

这样,上述代码就会报错了
在这里插入图片描述

析构函数

概念

析构函数不是对对象的销毁,而是完成销毁后对对象里的资源清理工作,对象的销毁是由编译器自己进行的

特点

1.析构函数名是类名前加一个~
2.析构函数没有参数也没有返回值,自然就无法构成重载
3.一个类只能有一个析构函数,若没有显式定义析构函数,则编译器会自动生成默认析构函数
4.对象生命周期结束时,编译器会自动调用析构函数

#include<iostream>
using namespace std;
class Date
{
public:
	Date()
	{
		_year = 2024;
		_month = 4;
		_day = 14;
	}
	~Date()
	{
		_year = 0;
		_month = 0;
		_day = 0;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	return 0;
}

5.编译器生成的默认析构函数和默认构造函数一样,对于自定义类型,编译器都会自动调用对应的默认析构函数
6.如果类中没有申请资源时,析构函数可以不写,但如果有申请时,就必须写析构函数,否则就会造成内存泄露

拷贝构造

概念

只有单个形参,编译器在创建新对象的时候会自动调用,这个形参必须是和新生成对象一样的类型

特点

1.拷贝构造是构造函数的一个重载函数
2.在调用拷贝构造创建新变量的时候,必须使用引用,否则会引发无穷递归调用,因为函数在传参的时候会调用拷贝构造创建形参,所以会无穷尽递归
3.若未显式定义,编译器会生成默认拷贝函数,默认构造函数会按内存储存字节进行字节序拷贝,这种拷贝称为浅拷贝/值拷贝,这种方式有一个缺点,比如说当你初始化一个字符串s1,然后初始化一个s2,如果你执行s1=s2这个操作,那当你改变s1里元素的时候,s2里元素也会跟着改变,这还有一个问题,因为类是有析构函数的,当你程序结束的时候,这个位置会被析构两次,程序就崩了
4.编译器自动生成的默认拷贝构造函数中,内置类型是使用字节拷贝构造的,而自定义类型是调用自定义类型的拷贝构造函数进行拷贝

//拷贝构造
string(const string& str)
	:_size(strlen(str._str))
	,_str(new char[1])
{
	delete[] _str;
	_capacity = _size;
	char* tmp = new char[_capacity + 1];
	strcpy(tmp, str._str);
	_str = tmp;
}

若没有这个自定义函数,两个不同对象的_str会指向同一块空间,动其中一个对象就会影响到另外一个对象。
5.关于什么时候需要写拷贝构造:如果涉及到资源的申请就需要写拷贝构造函数进行深拷贝,否则只需要用编译器自动生成的浅拷贝即可。

应用场景

1.使用已创建对象初始化新对象

string str1;//旧对象
string str2(str1);

2.函数参数类型为类类型对象

void Func(string str);//会调用拷贝构造

3.函数返回值类型为类类型对象

string func()
{
	string str;
	return str;
}

注:为了提高传参效率,能传引用就尽量不要用传值调用。

运算符重载

特点

用法

函数名字:operator后接需要重载的运算符符号
函数原型:返回值 operator 需要重载的运算符符号(参数列表)
下图用日期类Date作为例子

ostream& operator<<(ostream& out,Date& d1)
{
	out<<d1.year<<d1.month<<d1.day<<endl;
	return out;
}
注意

1.重载操作数必须有一个类类型参数
2.内置类型的运算符不能改变其含义,比如说内置类型的加减乘除,不能通过运算符重载改变它们的含义
3.作为类类型成员重载时,参数少1,有一个参数是隐藏的this指针
4.“*”“::”“sizeof”“?:”“.”这五个运算符不能重载
5.赋值运算符必须重载为类的函数,不能放在全局变量里

Date& operator=(Date& d1,Date& d2)
{
	if(&d1!=&d2)
	{
		d1.year=d2.year;
		d1.month=d2.month;
		d1.day=d2.day;
	}
	return d1;
}

若你去测试这一行代码,你会发现vs编译器底下会报一个错: error C2801: “operator =”必须是非静态成员,原因是用户如果在类中没有显式实现一个赋值运算符重载,编译器会默认生成一个浅拷贝赋值运算符重载,这时如果用户在类外又实现一个运算符重载,就会与编译器中默认生成的函数冲突,所以赋值运算符只能是类内的函数。
6.如果没有涉及到资源拷贝,那么赋值运算符实现和不实现都可以,但一旦涉及到资源申请,就必须实现赋值运算符重载,否则会导致某一些指针指向同一块空间,这个时候当你操作其中一个对象就会影响到另外一个对象,而且有析构两次的问题(一个类里面的成员有包含别的类,没有进行深拷贝就会析构两次,造成程序崩溃)
7.前置++,–与后置++,–的问题:因为前置++和后置++运算符都一样,只是使用的位置不一样,为了区分这两种,c++规定前置++可以和平常的运算符重载一样使用,而若要实现后置++的运算符重载,就需要在参数列表里加一个int

Date operator++();//前置++
Date operator++(int)//后置++

const成员

概念

const修饰的成员函数统称为const成员函数,const修饰成员函数,实际上是修饰该成员函数的隐藏this指针,表明在该成员函数中不能对类的任何成员进行修改

class Date
{
public:
	void Print() const//等价于 void Print(const Date* this)
	{
		cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
	}
private:
	int _year;
	int _month;
	int _day;
}
特点

const成员函数可以调用const成员函数,但不能调用非const成员函数(读写权限被放大),而非const成员函数可以调用const成员函数和非const成员函数
const对象不能调用非const成员函数(读写权限放大),而非const对象两种都可以调用。

取地址运算符重载

一般不自己实现,编译器会自动实现,下图是日期类的一部分

Date* operator&()
{
	return this;
}
const Date* operator&() const
{
	return this;
}

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

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

相关文章

OpenSPG v0.0.3 发布,新增大模型统一知识抽取图谱可视化

基于非结构化文档的知识构建一直是知识图谱大规模落地的关键难题之一&#xff0c;4 月 23 日&#xff0c;OpenSPG 发布 v0.0.3 版本&#xff0c;正式发布了大模型统一知识抽取功能&#xff0c;可大幅降低领域知识图谱的构建成本。还可用于增强大模型缓解幻觉并提升稳定性&#…

Spring Boot中判断轨迹数据是否经过设置的打卡点,且在PGSQL中把点拼接成线,判断某个点是否在线上或在线的50米范围内

问题描述 轨迹数据判断是否经过打卡点&#xff0c;轨迹数据太多&#xff0c;循环判断的话非常消耗内存。解决办法只需要把所有轨迹数据点拼成了一条线&#xff0c;然后只需要循环打卡点即可&#xff0c;打卡点不多&#xff0c;一般不会超过100个&#xff0c;如果多的话&#x…

C++高级特性:异常概念与处理机制(十四)

1、异常的基本概念 异常&#xff1a;是指在程序运行的过程中发生的一些异常事件&#xff08;如&#xff1a;除数为0&#xff0c;数组下标越界&#xff0c;栈溢出&#xff0c;访问非法内存等&#xff09; C的异常机制相比C语言的异常处理&#xff1a; 函数的返回值可以忽略&…

C++ | Leetcode C++题解之第41题缺失的第一个正数

题目&#xff1a; 题解&#xff1a; class Solution { public:int firstMissingPositive(vector<int>& nums) {int n nums.size();for (int i 0; i < n; i) {while (nums[i] > 0 && nums[i] < n && nums[nums[i] - 1] ! nums[i]) {swap(…

助力实现更可持续未来的智能解决方案:AI如何改变世界

人工智能已然成为今年的热门话题。由于生成式AI应用的快速采用&#xff0c;新闻头条充斥着有关AI如何彻底改变我们的政策制定、就业和经济走向的预测。您知道AI也是我们应对各种可持续发展挑战的先锋吗&#xff1f;AI通过分析大量数据&#xff0c;并提供有用的见解和工具&#…

Python | Leetcode Python题解之第44题通配符匹配

题目&#xff1a; 题解&#xff1a; class Solution:def isMatch(self, s: str, p: str) -> bool:def allStars(st: str, left: int, right: int) -> bool:return all(st[i] * for i in range(left, right))def charMatch(u: str, v: str) -> bool:return u v or v…

半波整流220V转正5V负-5V100mA恒压WT5101A

半波整流220V转正5V负-5V100mA恒压WT5101A WT5101A 是一款专为 Buck 和 Buck-Boost 拓扑而设计的高效、具有成本优势的离线恒压稳压器&#xff0c;内嵌有500V MOSFET。在降低系统成本的同时&#xff0c;这款稳压器只需少量的外部元件就能输出默认的5V电压。在轻负载条件下&…

30kw 直流回馈老化测试负载箱的优点

直流回馈老化测试负载箱是一种专门用于对电源设备进行老化测试的设备&#xff0c;其主要优点如下&#xff1a; 直流回馈老化测试负载箱采用了先进的直流回馈技术&#xff0c;能够将测试过程中产生的电能回馈到电网中&#xff0c;大大提高了能源的利用效率。这种技术不仅能够节…

水电气能耗管理云平台

安科瑞薛瑶瑶18701709087/17343930412 能耗管理云平台采用泛在物联、云计算、大数据、移动通讯、智能传感器等技术手段可为用户提供能源数据采集、统计分析、能效分析、用能预警、设备管理等服务&#xff0c;平台可以广泛应用于多种领域。

微信小程序按钮点击时的样式hover-class=“hover“

小程序的button组件很好用&#xff0c;按钮点击的时候会显示点击状态&#xff0c;默认的就是颜色加深 但是我们改变了button的背景色之后&#xff0c;就看不出点击效果了&#xff0c;解决起来也很简单 关键代码就是小程序的 hover-class 属性&#xff0c;需要注意的是&#xff…

【java】27:java绘图

坐标体系 - 介绍&#xff1a; 下图说明了Java坐标系。坐标原点位于左上角&#xff0c;以像素为单位。在Java坐标系中&#xff0c;第一个是x坐标&#xff0c;表示当前位置为水平方向&#xff0c;距离坐标原点个像素&#xff1b;第二个是y坐标&#xff0c;表示当前位置为垂直方向…

APP自动化测试-Android SDK SDK Manager.exe或者uiautomatorviewer.bat打不开,点击就一闪而已的原因

原因是找不到Java.exe的路径&#xff0c; 如果是uiautomatorviewer.bat打不开&#xff0c;则使用文本编辑器打开它&#xff0c;然后添加java安装路径 set java_exeC:\Program Files\Java\jdk1.8.0_321\bin\java.exe 同理&#xff1a; 如果是SDK Manager.exe和AVD Manager.ex…

2024全新Java学习路线图一条龙(视频+课件+源码资料)

前言 互联网浩瀚无际,你能来到这里,是机遇也是缘分,机遇,就像我的标题一样,你找到了一份Java 360度无死角的 Java 学习路线,而缘分让我们相遇,注定给你的学习之路搭上一把手,送你一程。 整条线路除了拥有后端整个技术体系外,还涵盖了前端、大数据、云计算、运维等各…

【telnet 命令安装】centos8 linux下安装telnet命令

在CentOS 8上安装Telnet服务&#xff0c;您需要分别安装Telnet客户端和服务器端。以下是安装步骤的概述&#xff1a; 检查是否已安装Telnet&#xff1a; 您可以使用rpm命令来检查系统是否已经安装了Telnet客户端或服务器端。例如&#xff1a; rpm -qa | grep telnet-client rpm…

OWASP发布十大开源软件安全风险及应对指南

​ 最近爆发的XZ后门事件&#xff0c;尽管未酿成Log4j那样的灾难性后果&#xff0c;但它再次敲响了警钟&#xff1a;软件供应链严重依赖开源软件&#xff0c;导致现代数字生态系统极其脆弱。面对层出不穷的安全漏洞&#xff0c;我们需要关注开源软件 (OSS)风险 &#xff0c;改进…

Web前端笔记1.0【Html详解,CSS详解】【Js待完善】

Web前端 html5 html5是构建web前端内容的一种语言描述方式 标签 标签分类 单标签&#xff1a;<input type text"> 双标签语法规范&#xff1a;<标签名> <标签名> 内容 </标签名></标签名> (可相互嵌套&#xff09; 常用标签 标题标…

在不使用PageHelper或Mybatis的情况下实现手动分页

有些特殊情况下我们不需要查库但前端数据需要分页&#xff0c;我们就需要手写分页实现&#xff0c;比如在线用户的功能我们可能会去Redis中查有哪些用户&#xff0c;然后返回给前端&#xff0c;但前端需要分页处理&#xff0c;我们就需要实现手动分页&#xff0c;话不多说直接上…

超越边界:如何ChatGPT 3.5、GPT-4、DALL·E 3和Midjourney共同重塑创意产业

KKAI&#xff08;kkai人工智能&#xff09;是一个整合了多种尖端人工智能技术的多功能助手平台&#xff0c;融合了OpenAI开发的ChatGPT3.5、GPT4.0以及DALLE 3&#xff0c;并包括了独立的图像生成AI—Midjourney。以下是这些技术的详细介绍&#xff1a; **ChatGPT3.5**&#xf…

vue 请求php接口 header 传自定义参数 提示cors 跨域问题

前端地址 http://192.168.0.125:4021 请求后端地址的时候报 from origin http://192.168.0.125:4021 has been blocked by CORS policy: Request header field userid is not allowed by Access-Control-Allow-Headers in preflight response. 大概意思是请求 header里有个…

递归算法/斐波那契数列

目录 递归 直接递归 间接递归 思想沿用 斐波那契数列 图片来源网络&#xff0c;侵权联系可删 递归 递归&#xff08;Recursion&#xff09;是一种编程技术&#xff0c;其中函数或方法直接或间接地调用自身。递归通常用于解决可以分解为更小、更简单的子问题的问题。递归的…