【C++】类和对象(中上):类的六个默认成员函数——构造函数、析构函数、拷贝构造函数!

news2024/11/25 23:52:11

目录

前言:

一、类的默认成员函数:

二、构造函数:

1.特性:

构造函数调用规则:

1.无参数的构造函数(默认构造函数):

2.带参数的构造函数:

3.全缺省的构造函数(默认构造函数):

三、析构函数:

特性:

 四、拷贝构造函数:

总结:


前言:

        上一篇文章我们浅浅的了解类与对象,知道了如何定义一个类,而今天我们更加深入的了解类与对象,也就是了解类的六个默认成员函数。接下来让我们进入学习。

一、类的默认成员函数:

        如果一个类中什么都没有,称之为空类。虽然称之为空类,但它并不是真正意义上的空类,虽然我们没有给它写任何东西,但是编译器会自动的生成六个默认成员函数。

默认成员函数:用户没有显示实现时,编译器自动生成的成员函数叫做默认成员函数。

默认成员函数有6个可以分为三类:

①.初始化和清理:

构造函数主要完成初始化工作;

析构函数主要完成清理工作。

②.拷贝复制:

拷贝构造是使用同类对象初始化创建另一个对象;

赋值重载是把一个对象赋值给另一个对象。

③.取地址重载:

主要是普通对象和const对象取地址。

        本篇博客我们重点介绍前面三个默认成员函数:构造函数、析构函数、拷贝构造函数

二、构造函数:

        大家回想一下之前写一个Stack的时候,是不是有一个Init函数用来初始化,但是我们在使用的时候又可能忘记使用这个函数导致忘记初始化,编译器就会报错。构造函数就是为了避免这种错误发生而生的。

1.特性:

        构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开 空间创建对象,而是初始化对象。

构造函数特征如下:

①.名字与类名相同;

②.没有返回值;

③.对象实例化时编译器自动调用;

④.可以构成重载;

下面我们来看看下面这段代码:

class Date
{
public:
	Date()
	{
		cout << "Date()" << endl;
	}
	Date(int year, int month, int day)
	{
		cout << "Date(int year, int month, int day)" << endl;
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2(2022, 5, 22);
	return 0;
}

代码运行结果:

构造函数调用规则:

1.无参数的构造函数(默认构造函数):

class Date
{
public:
	Date()
	{
		cout << "Date()" << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;

	return 0;
}

        在这函数中,构造函数并没有传入参数,所以叫做无参数的构造函数。只要示例化就会调用这个函数。

2.带参数的构造函数:

class Date
{
public:

	Date(int year, int month, int day)
	{
		cout << "Date(int year, int month, int day)" << endl;
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d2(2022, 5, 22);
	return 0;
}

        在这个函数中,构造函数传入了参数,所以叫带参数的构造函数,只需向调用函数一样就可以。

3.全缺省的构造函数(默认构造函数):

class Date
{
public:

	Date(int year=1, int month=2, int day=20)
	{
		cout << "Date(int year, int month, int day)" << endl;
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2(2022, 5, 22);
	return 0;
}

        这和带参的构造函数差不多,也是一样的调用,有参数时候按照方式2来传参数,没有参数的时候按照方式1来调用。我们再来看看这段代码:

class Date
{
public:
	Date()
	{
		cout << "Date()" << endl;
	}
	Date(int year, int month, int day)
	{
		cout << "Date(int year, int month, int day)" << endl;
		_year = year;
		_month = month;
		_day = day;
	}
	Date(int year=1, int month=2, int day=20)
	{
		cout << "Date(int year, int month, int day)" << endl;
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2(2022, 5, 22);
	return 0;
}

为什么代码运行的时候为报错呢?

        虽然构造函数之间在语法上构成重载函数,但是我们在调用的时候,会存在调用不明确的问题,也就是说不知道该调用那个构造函数。

        当用户没有自己写构造函数的时候,编译器会自动生成一个构造函数,称之为默认构造函数,默认构造函数只能有一个。

ps:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。

我们来看看编译器生成的默认构造函数:

class Date
{
public:
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d2();
	return 0;
}

 

我们可以看到编译器生成的默认构造函数,但是看起来默认构造函数又没什么用?d2对象调用了编译器生成的默认构造函数,但是d对象 _year/_month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用??

解答:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如: int/char...,自定义类型就是我们使用class/struct/union等自己定义的类型。也就是说默认构造函数,对内置类型成员不做处理,自定义类型成员会去调用它的默认构造(不用传参数的构造);

三、析构函数:

        与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成鹅,而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

特性:

析构函数特性如下:

①.析构函数实在类名前加~;

②.无参数无返回值类型;

③.一个类只能有一个析构函数,若用户未显示定义,系统会自动生成默认的析构函数。析构函数不支持重载;

④.对象生命周期结束时,c++编译系统自动调用析构函数。

class Stack
{
public:
	Stack(int capcity = 4, int top = 0)
	{
		_capcity = capcity;
		_top = top;
	}
	~Stack()
	{
		cout << "~Stack()" << endl;
	}
private:
	int _capcity;
	int _top;
	int* _a;
};

int main()
{
	Stack d;

	return 0;
}

        可以看到我们并没有去调用析构函数,编译器自动帮我们调用了析构函数。默认生成的析构函数与默认生成的构造函数一样,对内置类型成员不做处理。自定义类型成员会去调用它的默认析构函数。析构函数的调用遵循后进先出原则,也就说后定义的对象先进行析构函数的调用。

class Time
{
public:
	Time()
	{
		cout << "Time()" << endl;
	}
	~Time()
	{
		cout << "~Time()" << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};


class Date
{
public:
	

private:
	int _year;
	int _month ;
	int _day ;

	Time t1;

};

int main()
{
	Date d;
	return 0;
}

在main中没有创建Time类的对象,但还是打印了Time和~Time,可见析构函数确实会对自定义类型调用其自己的析构函数。(构造函数同理)

 四、拷贝构造函数:

只有单个形参,该形参时本类类对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

1.拷贝构造函数是构造函数的一个重载形式,一下是拷贝构造函数示例:

class Date
{
public:
	Date(int year = 1, int month = 2, int day = 12)
	{
		_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;
	}
	

private:
	int _year;
	int _month ;
	int _day ;

};

int main()
{
	Date d1(2023,5,23);
	Date d2 = d1;
	d1.Print();
	d2.Print();
	
	return 0;
}

         可以看到d2确实和d1的值是一样的,说明拷贝成功了。

拷贝构造函数参数中的const有什么用呢?

解答:这里加const是为了防止将谁是谁的拷贝的位置写反了,一旦写错不加const那么用来拷贝的那个类里面的东西都被修改了。这里加const还有第二个好处,就是防止权限放大。

2.拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。

为什么会引发无穷递归调用呢?

因为我们在传值传参时候,只要是自定义类型那么编译器会自动调用拷贝构造函数,举个例子:Date(Date d)如果这是拷贝构造函数,在调用这个函数的时候发现参数为自定义的Date类型那么就会调用拷贝构造函数,此时又出现了Date(Date d),所以会一直无穷的递归下去。

为什么使用引用传值就不会无穷递归?

因为引用就是本身,在调用也是本身不会去再调用拷贝构造函数。

3.当用户没有写拷贝构造函数时,编译器会自动生成拷贝构造函数。

class Date
{
public:
	Date(int year = 1, int month = 2, int day = 12)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
	

private:
	int _year;
	int _month ;
	int _day ;

};

int main()
{
	Date d1(2023,5,23);
	Date d2 (d1);
	d1.Print();
	d2.Print();
	
	return 0;
}

 通过上面的代码我们可以发现及时我们不写拷贝构造函数编译器也能完成日期类的拷贝,那要是其他类型呢?我们来看看下面这段代码:

class Stack
{
public:
	Stack(int top = 0,int capcity=4)
	{
		_a = (int*)malloc(sizeof(int*) * capcity);
		_top = top;
		_capcity = capcity;
	}
	
private:
	int* _a;
	int _top;
	int _capcity;

};
int main()
{
	Stack s1;
	Stack s2(s1);
	return 0;
}

从结果来看我们好像拷贝成功了,实际上并没有,s1和s2的_a其实是指向同一块地址的。当我们结束s1和s2对象时,_a会被清理两次。而且当我们的修改s1_a的内容时,s2的也会跟着修改。

为什么拷贝构造会造成上述这个情况?

在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定义类型是调用其拷贝构造函数完成拷贝的。

 注意:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。

什么情况下会调用拷贝构造呢?

1.显示调用

2.传值传参

3.自定义类型做返回值

总结:

        初学者对于构造函数,析构函数,拷贝构造函数时一大难点,所以需要下去多练习多思考。

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

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

相关文章

Qt编写视频监控系统75-计算实时码率并显示

一、前言 做监控摄像头的实时视频显示&#xff0c;一般还会要求统计实时码率显示在通道画面上&#xff0c;一个是为了测试下整个软件的性能&#xff0c;同时也看下当前到底是主码流还是子码流&#xff0c;设备到底是不是真的按照设定的码流大小来传输视频数据的。视频码率就是…

【Mysql】 数据类型

文章目录 【Mysql】 数据类型数据类型分类数值类型1. tinyint类型2. bit类型3. 小数类型 字符串类型1.char2.varchar3. 日期和时间类型4. enum 和 set 【Mysql】 数据类型 mysql中数据类型的作用&#xff1a; 约束操作者的行为更清晰的代码逻辑不同的功用 – 例如&#xff0c…

【JavaSE】Java基础语法(八)

文章目录 &#x1f353;1. 类和对象&#x1f379;&#x1f379;1.1 类和对象的关系&#x1f379;&#x1f379;1.2 类的定义 &#x1f353;2. 对象内存图&#x1f379;&#x1f379;2.1 单个对象内存图&#x1f379;&#x1f379;2.2 多个对象内存图2.3 多个对象指向相同内存图…

统计学_贾俊平——思考题第9章 分类数据分析

1&#xff0e;简述列联表的构造与列联表的分布。 答&#xff1a;列联表是将两个以上的变量进行交叉分类的频数分布表。 列联表的分布可以从两个方面看&#xff0c;一个是观察值的分布&#xff0c;又称为条件分布&#xff0c;每个具体的观察值就是条件频数&#xff1b;一个是期望…

【数据结构】树的认识

一个人的未来不是预测出来的&#xff0c;而是创造出来的。 -- 亚当詹姆斯目录 &#x1f341;前言&#xff1a; &#x1f340;一.什么是树&#xff1f; &#x1f351;二.树有什么用&#xff1f; ❤️1. 数据库 &#x1f9e1;2. 文件系统 &#x1…

chatgpt赋能python:PythonUSB摄像头-拍摄更美好的瞬间

Python USB摄像头 - 拍摄更美好的瞬间 在过去的几年中&#xff0c;摄影已经迅速成为了一种爆炸性的趋势。人们希望能够记录下人生中的美好瞬间&#xff0c;分享给全球的亲朋好友。而USB摄像头的普及与发展使得照片拍摄变得更加便利。而在这其中&#xff0c;Python也扮演了一个…

spingboot+jsp仓储型物流企业车辆运输管理系统

随着时代的进步,物流车辆运输行业也逐渐变得庞大起来。当然,物流车辆运输公司要想做大做强,就有必要有自己完整的一套物流车辆运输管理系统。这必将为物流管理公司提供规范化的管理模式,在各个部门之间有效的协调、合作过程中必将为物流车辆公司提供大量的客户生源,争取赢得最大…

6.4_7关键路径

上一节我们学的叫做AOV网&#xff08;activity on vertex&#xff09; 这一节我们是(activity on edge network) 顶点表示事件是一瞬间发生的事情。边上的权值表示完成该活动的开销。 AOE网中&#xff0c;有些事情是可以并行的。 前后活动之间存在依赖关系&#xff0c;我i们要知…

python绘制密度图

本期目录 1、绘图参数2、使用 matplotlib 库绘制密度图时常用的参数3、案例4、 运行结果python绘图往期系列文章目录 1、绘图参数 可以使用多种库来绘制密度图&#xff0c;其中最常用的是 seaborn 和 matplotlib。以下是使用 seaborn 库绘制密度图时常用的参数&#xff1a; i…

简历上,我写精通 JUC 的底气

真的假的&#xff0c;你简历上敢写精通 JUC &#xff1f; 是真学到精通了&#xff0c;还是说只学到了个皮毛就写精通&#xff0c;从而争取一个面试机会。 我相信&#xff0c;当很多人看到文章标题的第一反应也会如上面的一样&#xff0c;质疑、好奇。这很正常&#xff0c;如果…

手把手教你用Python调用彩云机器翻译API

一、引言 彩云这个小而美的机器翻译一直很低调&#xff0c;它让人眼前一亮的是之前我们分享的网页翻译插件&#xff0c;可以把外文网站翻译成英中对照的样式&#xff0c;便于我们学习。之前我们也写过文章介绍过&#xff1a; PythonFan&#xff1a;如何用Google翻译英文网页成…

c++学习——类和对象

类和对象的基本概念 类是自定义数据类型&#xff0c;是C语言的结构体进化而成的 对象是类实例化出的&#xff0c;用数据类型定义一个变量 #define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std;class Maker //这个是类 { public:int a;//成员属性…

PostgreSQL EDB 公司推出新服务,ORACLE 平移到 POSTGRESQL 一体化服务

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…

创建本地LocalHost-SSL证书

mkcert 使用方法 mkcert 是一个开源工具&#xff0c;用于快速生成有效的本地开发证书。它可以帮助开发人员在本地环境中使用 HTTPS 加密来模拟真实的生产环境。 安装 首先&#xff0c;你需要安装 mkcert 工具。以下是在常见操作系统上安装的命令&#xff1a; macOS 使用 Homebr…

集简云数据表无需代码连接抖音的方法

使用场景 抖音作为自媒体时代的主流平台&#xff0c;越来越多的企业选择通过短视频来推广自己的产品或者吸引更多粉丝。那么随时关注抖音视频下的评论&#xff0c;了解用户的想法和需求&#xff0c;并针对不同的评论提供更好的回应是每一位运营的重点工作之一&#xff0c;但是运…

利用 PRIMO 重构 M87 黑洞图像,普林斯顿高等研究院成功将「甜甜圈」变身「金戒指」...

By 超神经 内容一览&#xff1a;2019 年&#xff0c;「事件视界望远镜 (Event Horizon Telescope&#xff0c;简称 EHT)」全球研究团队发布了人类历史上第一张黑洞照片&#xff0c;受限于当时的观测条件&#xff0c;这张黑洞图像只呈现出一个模糊不清的轮廓。近日&#xff0c;天…

研发工程师玩转Kubernetes——自动扩缩容

在《研发工程师玩转Kubernetes——使用Deployment进行多副本维护》一文中&#xff0c;我们通过Deployment实现了多副本维护——即维持在一个确定数量的副本个数。而在现实场景中&#xff0c;我们往往需要根据服务的压力&#xff0c;采用水平&#xff08;横向&#xff09;扩容的…

分享给你这几款冷门好用的工具

分享一&#xff1a;Hi HiSlide Hi HiSlide是一个在线演示文稿制作工具&#xff0c;它可以帮助用户轻松地创建出色的演示文稿。以下是该网站的一些功能和特点&#xff1a; 多种模板选择**&#xff1a;Hi HiSlide提供了多种演示文稿模板&#xff0c;适合不同行业和场合的演示需…

chatgpt赋能python:Python%.6f:一门强大且易于学习的编程语言

Python%.6f&#xff1a;一门强大且易于学习的编程语言 Python是一门免费开源的高级编程语言&#xff0c;用于快速开发脚本、Web应用、科学计算、数据分析、人工智能等应用程序。Python的灵活性、易学性、可读性和强大的第三方库使其成为全球最受欢迎的编程语言之一。 Python的…

面对当下各种不确定性,如何面对,每天很忙碌,不慌

&#xff08;点击即可收听&#xff09; 疫情时期,都难,疫情之后,发现还更难 随着互联网的热度的下降,各大小公司纷纷勒紧裤腰带,受打击最大的无疑是底层打工人 每天一打开手机,会发现,一些大厂裁员信息霸榜头条,年龄也是一道坎 刚刚看到一个大v发的&#xff1a; 一个原先是跨国…