c++类和对象(6个默认成员函数)第二级中阶

news2024/11/13 11:04:26

目录

6个默认成员函数介绍

构造函数

构造函数是什么?

构造函数的6种特性

析构函数

析构函数是什么?

析构函数的特性

拷贝构造函数

什么是拷贝构造函数

拷贝函数的特性 

四.默认生成的拷贝构造实行的是浅拷贝(值拷贝)(重点)

赋值操作符重载

运算符重载:

赋值运算符重载(默认成员函数) 

 赋值运算符的tips

const和普通取地址操作符重载


6个默认成员函数介绍

class Date {
}; //空类

之前有说到如果一个类什么都没有是一个空类,但是空类为什么和只有函数的类一样只有1字节的占位符?这就说明了空类只是看着是空,其实其中大有乾坤。

 

顾名思义默认成员函数,那就表示了这些函数如果你没写就自动生成,当然你也可以自己写。 

构造函数

构造函数是什么?

名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有 一个合适的初始值,并且在对象的生命周期内只调用一次

下面的代码是一个有关日期的类:

class Date
{
public:
	Date(int year = 0, int month = 1, int day = 1)// 构造函数
	{
        //利用参数初始化成员变量
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "年" << _month << "月" << _day << "日" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

 注意:构造函数的主要任务并不是开空间创建对象,而是初始化对象。(开空间是创建类对象时进行的,构造函数是在空间开好后进行的)

构造函数的6种特性

一.构造函数的函数名和类名相同

二.构造参数没返回值

不是void!!!!!!,而是什么返回值都没有,可以这样理解,构造函数是构造这个对象。可以想象成返回的就是一个实例(默认的而不是)

三.对象实例化时对象自动调用对应的构造函数

创建一个对象时会自动调用一个适合的构造函数 (对应四)

四.构造函数可以重载

重载就是可以有同名函数只要参数不同即可(不同的参数,用不同的构造)

五.无参的构造函数和全缺省的构造函数以及不写构造函数都是默认构造函数,默认构造函数只有一份。(三选一)

默认构造函数只能有1份。但四说的重载是指的除了默认构造函数。

六.如果类中没有写出构造函数,则自动生成一个无参的构造函数并使用,如果写了则不生成

那这样不是都用默认的就好了,这样固然方便,但是初始化的值是随机值。

析构函数

析构函数是什么?

与构造函数功能相反,析构函数负责完成对象的销毁,对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。

默认析构函数可以释放局部资源的空间,但是如果在函数内有动态开辟的new的空间,则需要程序员自己写delete释放对应的空间。

析构函数的特性

一.析构函数名和构造函数名类似,在前面+'~'

class Date
{
public:
	Date()// 构造函数
	{}
	~Date()// 析构函数
	{}
private:
	int _year;
	int _month;
	int _day;
};

二 .无返回值 无参数。

与构造函数不同的是析构函数不能有参数。因为不需要参数。

三.使用对象的生命周期结束,自动调用

int main()//main 函数的生命周期在return 后 结束
{
	Date d1(1, 1, 1);//创造d1变量
	return 0;
}

main函数在return后生命周期结束,d1自动调用析构函数。 

四.一个类有且只有一个析构函数,如果没写会自动生成默认析构函数。

因为析构函数没有参数,所以实现不了重载和缺省。所以析构函数只有一个。显示写出来则用写出来的,如果没写则用自动生成的。

五.类似栈的构造析构调用

构造时把对象放入栈,析构时根据弹出顺序析构

拷贝构造函数

什么是拷贝构造函数

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

#include <iostream>
using namespace std;
class Date
{
public:
	Date(int year = 0, int month = 1, int day = 1)// 构造函数
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date& d)// 拷贝构造函数
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2021, 5, 31);
	Date d2(d1); // 用已存在的对象d1创建对象d2

	return 0;
}

由上述代码看出,拷贝构造函数和类名相同,参数是类对象的引用。顾名思义就是用另一个对象初始化当前对象。

拷贝函数的特性 

一.拷贝函数是构造函数的重载

和构造函数只有参数不同,所以是一个重载

二.拷贝函数的参数只有一个并且必须是引用参数

	Date(const Date d)// 拷贝构造函数
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

函数传参时,如果没有限定是指针或者引用,那就是传值传参,对于这类传参,系统会先对传入的参数进行一个拷贝,然后拷贝出来的这一份复制体在函数中使用,对于拷贝函数而言,如果传值传参在传入参数时它又要拷贝一遍,这个拷贝调用的还是拷贝构造函数,这样就陷入了死循环包出错的。

tips:传值传指针传引用详解(之后加上)

三.未显示定义则会默认生成

默认成员函数:如果没有就用默认的

四.默认生成的拷贝构造实行的是浅拷贝(值拷贝)(重点)

深拷贝和浅拷贝:这样的情况通常在于成员变量中存在指针和用自己开辟空间的时候。对于普通的内置类型变量(int char等等)浅拷贝和深拷贝没区别,内置类型在栈上开辟空间,而new的空间是在堆上。

shallow copy浅拷贝:由图可知我们浅拷贝时对应 的新对象和老对象指向的node是一样的。说明了浅拷贝就是把老对象的东西从根本上挖过来,属于是两人一起用。

class ShallowCopyExample {  
private:  
    int* data;  
public:  
    ShallowCopyExample(int value) {  
        data = new int(value);  
    }  
  
    // 浅拷贝构造函数(默认实现)  
    ShallowCopyExample(const ShallowCopyExample& other) {  
        data = other.data; // 浅拷贝,只是复制了指针  
    }  
  
    ~ShallowCopyExample() {  
        delete data;  
};

上述代码也表示了因为是引用传参所以复制出来的data指针是相同的。 

deep copy深拷贝:则是重新拷贝出来一份新的。一对双胞胎,老大买了一份吃的,老二就也要买一份一模一样的。两人用的是相同的,但不是一起用,这就是深拷贝。

class DeepCopyExample {  
private:  
    int* data;  
public:  
    DeepCopyExample(int value) {  
        data = new int(value);  
    }  
  
    // 深拷贝构造函数  
    DeepCopyExample(const DeepCopyExample& other) {  
        data = new int(*other.data); // 分配新内存并复制值  
    }  
  
    ~DeepCopyExample() {  
        delete data;  
    }  
}; 

上述代码中,和浅拷贝不同的点在于有指针的情况下, 我们new了一个新的int。这样空间就是新的,二者用的就是不一样的空间。

两种形式可以按需使用,但如果有自己使用new等函数开辟的空间时一定要使用深拷贝,如果是浅拷贝因为大伙用的是一片空间在free时会出现多次free的情况。

赋值操作符重载

运算符重载:

之前对于两个自定义类型要做比较时,都是做一个函数,然后调用这个函数来进行比较就像strcmp

运算符重载就是为了让自定义类型也能够通过 == + - 等等运算符来进行比较和运算。

d1 == d2;// 可读性高(书写简单)
IsSame(d1, d2);// 可读性差(书写麻烦)

运算符重载函数长什么样子:和普通函数类似。只是函数名要注意:operator + 符号构成函数名

 注意事项:

1.不能新建符号,自定义类型有的才能创建比如 operator~,没有这个用法

2.重载操作符要操作的对象至少要存在一个自定义类型

3.本身作用于内置类型(int,char等等)的操作符,重载过后效果不变

4.如果操作符重载放在类内,那也是作为成员函数也会有this指针。

5.sizeof 、:: 、.* 、?: 、. 这5个运算符不能重载。

以operator==为例:

class Date
{
public:
	Date(int year = 0, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "年" << _month << "月" << _day << "日" << endl;
	}
	bool operator==(const Date& d)// 运算符重载函数
	{
		return this._year == d._year
			&&this._month == d._month
			&&this._day == d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};
bool operator==(const Date& d1, const Date& d2)// 运算符重载函数
{
	return d1._year == d2._year
		&&d1._month == d2._month
		&&d1._day == d2._day;
}

在类内时参数只用传一个作为右比较数,因为还有一个this指针指向左比较数。 

赋值运算符重载(默认成员函数) 

和其他的运算符重载不同,赋值运算符是默认的。所以不写的话会由系统生成。

Date& operator=(const Date& d)// 赋值运算符重载函数
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}

 赋值运算符的tips

1.参数引用,并且加上const。

参数引用,如果不是引用而是传值的话,会在调用一次拷贝构造,造成多余损耗。

但因为是引用的参数,我们是要让右边对象(参数)的值赋值给左边的对象(this指针),所以参数+const,可以防止右边的对象在使用时被更改,提高安全性。

2.返回值用引用

返回值引用,因为这是一个函数,最终还是需要一个返回值,对于赋值而言,我们把参数的值全都复制了一遍,那现在这个this就是一个d的复制品,所以返回*this,就等于是把复制体返回了。如果传值返回,和参数引用一样会再拷贝一份,损耗变高

综上12条用引用都是为了降低损耗。

3.本体判断

如果传入的参数的地址 == this,那还赋值啥直接返回就可以了。

4.如果没写赋值重载,则使用系统生成的浅拷贝赋值重载

和拷贝构造一样,如果我们对象中包含自己new的空间或者指针,使用浅拷贝会让对象指向一块内存。

比较两份代码:

	Date d1(2021, 6, 1);
	Date d2(d1);
	Date d3 = d1;

对于d2而言 就是纯正的拷贝构造函数。d3看似是赋值。但是拷贝和赋值都有一定的前提

拷贝构造:是用已存在的对象的值拷贝给一个新的对象。 老对新

赋值重载:是用已存在的对象的值赋值给一个存在的对象。老对老

const和普通取地址操作符重载

class Date
{
public:
	Date* operator&()// 取地址操作符重载
	{
		return this;
	}
	const Date* operator&()const// const取地址操作符重载
	{
		return this;
	}
private:
	int _year;
	int _month;
	int _day;
};

也是用operator加符号当做函数名。但一般取地址重载不用自己写,系统默认的即可,因为 取地址返回的是一个地址,返回this指针即可。因为取地址不会出现浅拷贝,因为没有涉及值的赋值,所以没有风险。

前面的const是保证传出去的值的内容不被更改,函数后面的const是保证成员变量在这个函数不能被更改。

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

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

相关文章

【2024】前端学习笔记9-内部样式表-外部导入样式表-类选择器

学习笔记 内部样式表外部导入样式表类选择器&#xff1a;class 内部样式表 内部样式表是将 CSS 样式规则写在 HTML 文档内部。通过<style>标签在 HTML 文件的<head>部分定义样式。 简单示例&#xff1a; <!DOCTYPE html><html><head><style…

【linux】基础IO(上)

1. 共识原理 文件 内容 属性文件分为 打开的文件 没打开的文件打开的文件 &#xff1a; 是进程打开的 ----- 本质是要研究文件和进程的关系没打开的文件 &#xff1a; 没打开的文件储存在磁盘上&#xff0c;由于没打开的文件很多&#xff0c;所以需要分门别类的防止好&…

常用函数式接口的使用

FunctionalInterface注解 函数式接口在java中是指:有且仅有一个抽象方法的接口。 虽然知道怎么使用&#xff0c;但是没有搞懂使用场景&#xff0c;暂且记录下使用方法吧&#xff0c;不至于看到源码的时候不知所云。 要我自己写代码&#xff0c;我是想不起来这样用的&#xff0…

YOLOv9改进策略【注意力机制篇】| 2024 SCSA-CBAM 空间和通道的协同注意模块

一、本文介绍 本文记录的是基于SCSA-CBAM注意力模块的YOLOv9目标检测改进方法研究。现有注意力方法在空间-通道协同方面未充分挖掘其潜力&#xff0c;缺乏对多语义信息的充分利用来引导特征和缓解语义差异。SCSA-CBAM注意力模块构建一个空间-通道协同机制&#xff0c;使空间注…

C语言 结构体和共用体——典型实例:洗发牌模拟

目录 如何表示52张扑克牌&#xff1f; 如何保存一副扑克牌&#xff1f; 如何发牌&#xff1f; 如何设计主函数&#xff1f; 如何模拟洗牌&#xff1f; 如何表示52张扑克牌&#xff1f; 如何保存一副扑克牌&#xff1f; 如何发牌&#xff1f; 如何设计主函数&#xff1f; 如…

窗户检测系统源码分享

窗户检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vision …

十大常用加密软件排行榜|2024年好用的加密软件推荐(企业必备)

在数字化时代&#xff0c;数据安全已经成为企业生存和发展的关键因素之一。随着网络攻击和数据泄露事件的频发&#xff0c;企业对数据加密的需求日益增长。选择一款可靠的加密软件&#xff0c;不仅能保护企业的核心数据&#xff0c;还能确保业务的连续性和合规性。本文将为您介…

Stable Diffusion 使用详解(11)--- 场景ICON制作

目录 背景 controlNet 整体描述 Canny Lineart Depth 实际使用 AI绘制需求 绘制过程 PS打底 场景模型选择 设置提示词及绘制参数 controlnet 设置 canny 边缘 depth 深度 lineart 线稿 效果 背景 这段时间不知道为啥小伙伴似乎喜欢制作很符合自己场景的ICON。…

共享wifi哪家公司正规合法?看这3点就够了!

随着共享wifi项目的热度不断上升&#xff0c;越来越多的公司都开始加入到共享wifi贴码的研发行列之中&#xff0c;让意向入局该项目的创业者拥有更多选择的同时&#xff0c;也让许多想要借此割一波韭菜的不法分子有了可乘之机。在此背景下&#xff0c;共享wifi哪家公司正规合法…

OpenHarmony(鸿蒙南向开发)——小型系统内核(LiteOS-A)【内核启动】

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 鸿蒙&#xff08;OpenHarmony&#xff09;南向开发保姆级知识点汇总~ 子系统开发内核 轻量系统内核&#xff08;LiteOS-M&#xff09; 轻量系统内核&#…

Docker安装rabbitmq并配置延迟队列

下载rabbitmq镜像 docker pull rabbitmq:management 运行rabbitmq镜像 docker run -id --namerabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 15671:15671 -p 15672:15672 -p 25672:25672 -e RABBITMQ_DEFAULT_USERtom -e RABBITMQ_DEFAULT_PASStom rabbitmq:management …

回归传统,Domino拷贝式迁移!

大家好&#xff0c;才是真的好。 前面讲太多普及型的概念&#xff0c;今天我们来点实在的内容。 在Notes/Domino的黄金年代&#xff0c;有一件事情大家干得风生水起&#xff0c;那就是Domino服务器迁移。 要么迁移到另一台硬件服务器上&#xff0c;要么迁移到新换的磁盘当中…

展会上想要留住俄罗斯客户,柯桥成人俄语培训

展品 экспонат 模型 макет 证明(书) свидетельство 预算 бюджет 确认订单 подтверждение заказа 缺点,毛病,缺陷 недостаток 退换 возвращать 更换 заменять 调整 урегулир…

[PTA]7-1 谁管谁叫爹

[PTA]7-1 谁管谁叫爹 输入格式&#xff1a; 输入第一行给出一个正整数 N&#xff08;≤100&#xff09;&#xff0c;为游戏的次数。以下 N 行&#xff0c;每行给出一对不超过 9 位数的正整数&#xff0c;对应 A 和 B 给出的原始数字。题目保证两个数字不相等。 输出格式&…

虹科干货 | CAN/CAN FD故障揭秘:快速排查与解决技巧

是否在处理CAN总线问题时感到头疼&#xff1f;是否在寻找简单直接的方法来解决那些看似复杂的连接故障&#xff1f;本文将为您提供实用技巧&#xff0c;让您能够轻松应对这些难题。 CAN总线因其高效、可靠的数据交换能力&#xff0c;在汽车、工业控制、航空航天等多个关键领域得…

【软件方案】智慧社区总体解决方案(PPT原件)

1.智慧社区整体建设方案内容 2.整体功能介绍 软件全套资料部分文档清单&#xff1a; 工作安排任务书&#xff0c;可行性分析报告&#xff0c;立项申请审批表&#xff0c;产品需求规格说明书&#xff0c;需求调研计划&#xff0c;用户需求调查单&#xff0c;用户需求说明书&…

故障模拟测试负载是如何实现的

故障模拟测试负载是在系统或设备上故意引入故障&#xff0c;以测试其应对能力的方法。这种方法可以帮助我们了解系统在面临各种故障时的响应和恢复能力&#xff0c;从而提高系统的可靠性和稳定性。故障模拟测试负载的实现主要依赖于以下几个步骤&#xff1a; 1. 确定故障类型&…

uniapp快速入门教程,内容来源于官方文档,仅仅记录快速入门需要了解到的知识点

uniapp快速入门教程&#xff0c;内容来源于官方文档&#xff0c;仅仅记录快速入门需要了解到的知识点 目录 介绍uniapp 介绍uniapp x 介绍功能框架图创建项目&发布组件/标签的变化js的变化css的变化工程结构和页面管理 pages.jsonmanifest.json 应用配置组件easycom组件规…

【Unity杂谈】iOS 18中文字体显示问题的调查

一、问题现象 最近苹果iOS 18系统正式版推送&#xff0c;周围升级系统的同事越来越多&#xff0c;有些同事发现&#xff0c;iOS 18上很多游戏&#xff08;尤其是海外游戏&#xff09;的中文版&#xff0c;显示的字很奇怪&#xff0c;就像一些字被“吞掉了”&#xff0c;无法显示…

MongoDB解说

MongoDB 是一个流行的开源 NoSQL 数据库&#xff0c;它使用了一种被称为文档存储的数据库模型。 与传统的关系型数据库管理系统&#xff08;RDBMS&#xff09;不同&#xff0c;MongoDB 不使用表格来存储数据&#xff0c;而是使用了一种更为灵活的格式——JSON 样式的文档。 这…