类的默认成员函数(C++)

news2025/2/11 20:39:50

类的默认成员函数

  • 1.构造函数
    • 特性
  • 2.析构函数
    • 特性
  • 3.拷贝构造函数
    • 特性
  • 4.赋值重载函数
    • 运算符重载
    • 赋值运算符重载
  • const成员函数
  • 取地址运算符重载

1.构造函数

构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个成员函数都初始值,并且在对象的整个生命周期内只调用一次。

特性

构造函数是特殊的成员函数,需要注意的是,构造虽然名称叫做构造,但是构造函数的主要任务并不是开空间创造对象,而是初始化对象。
key:开空间是函数栈帧的事情,调用函数开辟空间。

  • 函数名必须与类名想同
  • 无返回值(void也不用写)
  • 对象实例化时自动调用对应的构造函数
  • 构造函数可以重载
class Time
{
public:
	//无参的构造函数
	Time()
	{
		cout << "None Pram" << endl;
	}


	//带参数的构造函数
	Time (int hour, int minute, int second)
	{
		_hour = hour;
		_minute = minute;
		_second = second;
		cout << _hour << ":" << _minute << ":" << _second << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};


int main()
{
	Time t1;  //调用无参的构造函数
	Time t2(9, 19, 30);  //调用带参的构造函数
	Time t3();   //该方法调用无参构造函数会报错,该种写法是写一个返回值为Time类型函数的写法。

	
}

在这里插入图片描述

  • 如果类中没有显示的定义构造函数,C++编译器会默认生成一个无参的默认构造函数,一旦用户显式定义编译器将不再自动生成。
class Time
{
public:
	带参数的构造函数
	//Time(int hour, int minute, int second)
	//{
	//	_hour = hour;
	//	_minute = minute;
	//	_second = second;
	//	cout << _hour << ":" << _minute << ":" << _second << endl;
	//}
private:
	int _hour;
	int _minute;
	int _second;
};


int main()
{
	//1.将显示定义的构造函数屏蔽以后,编译器会默认自动生成一个
	//无参的默认构造函数,因此不会报错。

	//2.如果将显示定义的构造函数展开,编译器不在生成,因为用户定义了一个。
	//此时如果还这样声明变量,就会报错,要以自己定义的方式传参。
	Time t1;  
}
  • 对于编译器生成的默认构造函数,实际上并没有对Time类的成员变量初始化,它究竟有什么用呢?
    编译器生成的默认构造函数对内置类型(int/char/double/指针)不做处理,对自定义类型(struct,class,union)等,会去调用它们自己的默认构造函数。(对内置类型不处理是C++的缺陷)。c++11又增加了一个特性,弥补了缺陷,即内置类型的成员变量在类中声明时可以给默认值

  • 无参的构造函数、全缺省的构造函数和没写编译器生成的构造函数,都可以认为是默认构造函数,默认构造函数有且只能有一个
    在这里插入图片描述

2.析构函数

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

特性

  • 析构函数名是在类名前加上字符~
  • 无参数无返回值类型
  • 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
  • 对象在生命周期结束时,C++编译器系统自动调用析构函数
class Stack
{
public:
	Stack(int capacity=3)
	{
		_arr = (int*)malloc(sizeof(int) * capacity);
		if (_arr == nullptr)
		{
			printf("malloc failed");
		}
		_capacity = capacity;
		_size = 0;
		printf("malloc success\n");
	}

	~Stack()
	{
		if (_arr)
		{
			free(_arr);
			_capacity = 0;
			_size = 0;
		}
		cout << "~stcak" << endl;
	}

private:
	int* _arr;
	int _capacity;
	int _size;
};


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

在这里插入图片描述

  • 当没有显示定义析构函数时,编译器会自动默认生成析构函数,它对内置类型不作处理,对自定义类型去调用它自己的析构函数。
  • 如果类中没有申请资源,可以不写析构函数,直接使用编译器默认生成的。否则会造成资源泄露。

3.拷贝构造函数

在创建对象时,可以创建一个与已存在对象一摸一样的新对象。该函数只有单个形参,该形参是本类类型对象的引用(常用const修饰)。

特性

  • 拷贝构造函数是构造函数的一个重载形式。
  • 拷贝构造函数的参数只有一个必须是类型对象的引用,使用传值方法会引起无穷调用,因为每一次传值调用,传过去的值都是一次拷贝构造(传值都会临时拷贝一份给形参传过去),这样会引发无穷递归。
class Time
{
public:
	//全缺省的构造函数
	Time(int hour=0, int minute=0, int second=0)
	{
		_hour = hour;
		_minute = minute;
		_second = second;
		cout << _hour << ":" << _minute << ":" << _second << endl;
	}

	//复制本应该有两个形参,为什么只写了一个?
	//因为还隐含了一个this指针。
	//使用const是防止传进来的d被修改
	Time(const Time& d)
	{
		this->_hour = d._hour;
		_minute = d._minute;
		_second = d._second;
	}
private:
	int _hour;
	int _minute;
	int _second;
};


int main()
{
	Time t1;
	Time t2(t1);  //拷贝构造的语法,获得一个以t1完全一样的t2
	
	return 0;
}

在这里插入图片描述


在这里插入图片描述
问题1:为什么不用=符号完成拷贝构造呢?
虽然d2、d3都能完成拷贝构造的任务,然而d1并不直观、不明确,容易让人误以为是赋值。
问题2:指针也能完成拷贝构造的任务,为什么要用引用呢?
d4传地址有点冗余麻烦,把d1的地址赋值给d5是什么意思?显然对比d2、d4、d5,发现使用引用是最符合正常人的逻辑的。

  • 若未显式定义,编译器会生成默认的拷贝构造函数。默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝或值拷贝
class TestCls {
public:
    TestCls()
    {
        cout << "TestCls()" <<endl;
        p = new int;
    }
    
    TestCls(const TestCls& d)
    {
        cout << "TestCls(const TestCls& testCls)" << endl;
        a = d.a;
        //p = testCls.p;   //如果是编译器默认生成的,会直接把指针的地址赋给p
        p = new int;

        *p = *(d.p);      //为拷贝类的p指针分配空间,实现深度拷贝
    }

    ~TestCls()
    {
        delete p;
        std::cout << "~TestCls()" << std::endl;
    }
private:
    int a;
    int* p;
};

int main()
{
    TestCls t1;
    TestCls t2(t1);

    return 0;
}

在这里插入图片描述

上面的例子也可以看出,像内置函数类型(简单的赋值操作),一般默认是深拷贝,但是涉及到指针的问题,涉及到自定义类的问题,我们在写构造函数时,一定要注意深浅拷贝的问题。


class Time
{
public:
	Time()
	{
		_hour = 1;
		_minute = 1;
		_second = 1;
	}
	Time(const Time& t)
	{
		_hour = t._hour;
		_minute = t._minute;
		_second = t._second;
		cout << "Time::Time(const 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 d1;
	return 0;
}

注意这段代码,d1的成员变量和_t的成员变量内存中是挨着的。
在这里插入图片描述

  • 类中如果没有涉及到资源申请时,拷贝构造函数写不写都可以;一旦涉及到资源申请时,必须写拷贝构造函数,否则就是浅拷贝(只赋给指针)

  • 拷贝构造典型场景
    使用已存在对象创建新对象;函数参数类型为类类型对象;函数返回值为类类型对象。

class Date
{
public:
 Date(int year, int minute, int day)
 {
 cout << "Date(int,int,int):" << this << endl;
 }
 Date(const Date& d)
 {
 cout << "Date(const Date& d):" << this << endl;
 }
 ~Date()
 {
 cout << "~Date():" << this << endl;
 }
private:
 int _year;
 int _month;
 int _day;
};
Date Test(Date d)
{
 Date temp(d);
 return temp;
}
int main()
{
 Date d1(2022,1,13);
 Test(d1);
 return 0;
}

在这里插入图片描述

因此,为了提高效率,一般对象传参时,尽量使用引用类型;返回时,根据实际场景,能用引用尽量使用引用。

4.赋值重载函数

运算符重载

C++为了增强代码的可读性引入运算符重载,运算符重载是具有特殊函数名的函数。
内置类型可以直接使用运算符运算,编译器知道如何运算;自定义类型无法直接使用运算符,编译器不知道如何运算,因此需要自己实现运算符重载。

函数定义语法:

返回值类型 operator操作符(参数列表)

  • 不能创建新的操作符,如:operator@
  • 重载操作符必须有一个类类型参数
  • 用作内置类型的运算符,其含义不能改变,例如:内置类型+,不能改变其含义。
  • 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this。
  • .*(见的很少) ::(域作用限定符) sizeof ?:(三目运算符) .(函数调用符号)注意以上5个符号不能进行函数重载
class Date
{
public:

	//构造函数
	Date(int year = 1970, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// 运算符重载
	//相当于bool operator==(Date* const this, const Date& d);
	//注意:这里左操作数是this,右操作数是d。
	bool operator==(const Date& d)
	{
		return _year == d._year
			&& _month == d._month
			&& _day == d._day;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2023, 8, 3);
	Date d2(d1);
	Date d3(1, 1, 1);

	//相当于
	//cout<<d1.operator==(&d1, d2)<<endl;
	cout << (d1 == d2) << endl;  //结果为1
	cout << (d1 == d3) << endl;		//结果为0
	return 0;
}

赋值运算符重载

即给一个已经存在的对象赋值(已经存在),拷贝构造(给一个新对象赋值)。

  • 参数类型:const T&,传递引用可以提高效率
  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  • 检测是否自己给自己赋值
  • 返回*this:保证连续赋值不出错。
// 赋值运算符重载
//参数加引用和const,传参更快,防止参数修改
//返回引用,防止拷贝构造。
	Date& operator=(const Date& d)
	{
		//防止  d1 = d1;
		if(this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}

		return *this;
	}

	//可以这样调用
	d2 = d1;  // d2.operator=(&d2, d1)   第一个参数是地址,第二个参数是传引用
	d3 = d2 = d1;
  • 赋值运算符只能重载成类的成员函数,不能重载成全局函数
    因为赋值运算符重载如果用户不显示的定义在类里面,编译器就在类里面自动生成一个。此时如果再在全局声明一个,就和类里面的冲突了。

  • 用户没有显示的实现时,编译器会默认生成一个默认赋值运算符重载,以值的方式逐字节拷贝。内置类型成员变量直接赋值,自定义类需调用自己实现的。

const成员函数

使用const修饰的成员函数
参考文章地址

取地址运算符重载

//写在类里面

	//取地址运算符重载
	Date* operator&()
	{
		return this;
	}
	//通过这样设置可以保证,类外面的成员只能访问地址,不能修改地址。
	const Date* operator&()const
	{
		return this;
	}

正常情况下,如果我们不写这个成员函数,编译器会自动生成。所以一般不需要我们自己去写。
那我们什么时候应该写这个成员函数呢?
比如当你不想让别人取到这个类型对象的地址的时候,你可以return nullptr ,这样就永远无法获得这个对象的地址了。

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

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

相关文章

怎么制作gif动态图?gif图片在线制作攻略分享

现在许多品牌和营销活动也使用gif动态图来吸引用户注意力、提升品牌形象或传递特定的信息&#xff0c;那么gif制作的过程到底难不难呢&#xff1f;其实只需要使用gif图片在线制作工具就非常简单了&#xff0c;下面以图片制作gif&#xff08;https://www.gif.cn&#xff09;为例…

一个事务插入,另外一个事务更新操作,是否会更新成功?

1.前言 同样另外一个非常有意思的题目&#xff0c;值得我们思考。大概背景是这个样子的。如果有一个事务A进行插入 id > 100, 同时另外一个事务B进行更新update id > 100。那么事务B是否会更新成功。我们来画一个时序图&#xff1a; time事务A事务B备注T1insert id >…

MPAS-A原理及陆面模式的基本概念

跨尺度预测模式&#xff08;The Model for Prediction Across Scales - MPAS&#xff09;是由洛斯阿拉莫斯实验室和美国国家大气研究中心(NCAR)共同开发&#xff0c;其由3个部分组成&#xff0c;分别称为 MPAS-A&#xff08;大气模型&#xff09;、MPAS-O&#xff08;海洋模型&…

Nginx+Tomcat负载均衡、动静分离实例详细部署

一、反向代理两种模式 四层反向代理 基于四层的iptcp/upd端口的代理 他是http块同一级&#xff0c;一般配置在http块上面。 他是需要用到stream模块的&#xff0c;一般四层里面没有自带&#xff0c;需要编译安装一下。并在stream模块里面添加upstream 服务器名称&#xff0c;…

uni-app日期选择器

写个简单的日期选择器&#xff0c;还没搞样式&#xff0c;所以有点丑 大概长这样吧 首先是这个picker选择器&#xff0c;mode选择日期&#xff0c;end是写一个范围前日期&#xff0c;:end就是这个日期是动态变化的&#xff0c;还有change函数 <template><view>&l…

LeetCode150道面试经典题--验证回文串(简单)

1.题目 如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后&#xff0c;短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。 字母和数字都属于字母数字字符。 给你一个字符串 s&#xff0c;如果它是 回文串 &#xff0c;返回 true &#xff1b;否…

通过这些case,我把项目LCP时间减少了1.5s

您好&#xff0c;如果喜欢我的文章&#xff0c;可以关注我的公众号「量子前端」&#xff0c;将不定期关注推送前端好文~ 前言 最近在做公司几个项目性能优化&#xff0c;整理出一些比较有用且常见的case来分享一下。 A项目优化 白屏相关 DNS预连接、资源预解析 对于公共域…

还不会python 实现常用的数据编码和对称加密?看这篇文章就够啦~

♥ 前 言 相信很多使用 python 的小伙伴在工作中都遇到过&#xff0c;对数据进行相关编码或加密的需求&#xff0c;今天这篇文章主要给大家介绍对于一些常用的数据编码和数据加密的方式&#xff0c;如何使用 python 去实现。话不多说&#xff0c;接下来直接进入主题&#xf…

install imap error

【错误翻译】 Try to run this command from the system terminal. Make sure that you use the correct version of pip installed for your Python interpreter located at D:\Program Files (x86)\Python\Python39\python.exe. 尝试从系统终端运行此命令。请确保使用安装在…

【UE4 RTS】09-Day and Night

前言 本篇博客实现的效果是太阳和天空会随着游戏时间的变化而变化。 效果 步骤 1. 设置“LightSource”为可移动的 2. 新建一个文件夹&#xff0c;命名为“Lighting” 3. 打开游戏状态“RTS_GameState_BP”&#xff0c;添加一个函数命名为“GetGameSpeed” 添加一个浮点类型…

利用 Splashtop Enterprise 改善公司的网络安全

在我们日益数字化的世界中&#xff0c;对强有力的网络安全措施的需求从未像现在这样迫切。随着组织扩大其数字足迹并采用远程办公解决方案&#xff0c;他们面临着一系列不断变化的挑战。 威胁行为者不断寻找利用漏洞的新方法&#xff0c;这使得企业保持领先地位至关重要。俗话…

【Cartopy】库的安装和瓦片加载(天地图、高德等)

原文作者&#xff1a;我辈李想 版权声明&#xff1a;文章原创&#xff0c;转载时请务必加上原文超链接、作者信息和本声明。 Cartopy基础入门 【Cartopy】库的安装和天地图瓦片加载 【Cartopy】【Cartopy】如何更好的确定边界显示 【Cartopy】【Cartopy】如何丝滑的加载Geojso…

搜狗输入法曝加密系统漏洞(已修复),黑客可窃取用户输入的内容

近期&#xff0c;来自加拿大多伦多大学公民实验室的研究人员在国内热门输入法——搜狗输入法的加密系统中发现了漏洞&#xff0c;能允许网络监听者破译用户的输入内容。目前该漏洞已得到修复。 研究人员发现漏洞的软件版本涉及三大主流系统&#xff0c;分别是Windows 13.4版本、…

教程分享:如何制作一个旅游路线二维码?

吃一成不变的早餐&#xff0c;九点出门还会遇见楼下遛狗的大爷&#xff0c;老板掐着表发起了会议邀请&#xff0c;窗外还是那几棵树&#xff0c;天空依旧灰蒙蒙的&#xff0c;羊了个羊第二关还是过不去&#xff0c;理发店的小哥又倚在门口抽烟…… 大多时候&#xff0c;我们的…

Small Tip: 如何Debug Start Routine

我也不知道咋地&#xff0c;在generated ABAP里面打断点进不去。 我也不晓得怎么弄&#xff0c;今天反正是硬找着去弄。不晓得有没有其他好办法。有知道的小伙伴评论下吧。 1、 在DTP里面选Before Transformation&#xff0c;要去debug start routine选这个就够了。其他的随意…

轻装上阵,不调用jar包,用C#写SM4加密算法【卸载IKVM 】

前言 记得之前写了一个文章&#xff0c;是关于java和c#加密不一致导致需要使用ikvm的方式来进行数据加密&#xff0c;主要是ikvm把打包后的jar包打成dll包&#xff0c;然后Nuget引入ikvm&#xff0c;从而实现算法的统一&#xff0c;这几天闲来无事&#xff0c;网上找了一下加密…

关于三维模型OSGB格式轻量化压缩出现分层现象分析

关于三维模型OSGB格式轻量化压缩出现分层现象分析 三维模型OSGB格式的轻量化压缩分层现象是指在进行几何压缩和纹理压缩过程中&#xff0c;由于不同部位的信息复杂度存在差异&#xff0c;可能会出现某些部位被处理过度&#xff0c;导致其失去了细节和质感的现象。本文将从几何…

“解锁IDEA的潜力:高级Java Maven项目配置指南”

目录 前言&#xff1a;流程目录&#xff1a;1.确保Java和Maven已安装检查Java是否已正确安装并配置环境变量 2.创建一个新的Maven项目导航到要创建项目的目录配置Maven运行以下命令创建一个新的Maven项目 3.配置项目的pom.xml文件打开项目根目录下的pom.xml文件配置Web.xml 4.配…

什么是绩效管理?绩效管理包括哪些内容?

阅读本文您可以了解&#xff1a;1、绩效管理的定义&#xff1b;2、绩效管理的内容&#xff1b; 一、什么是绩效管理 绩效管理是一种组织和管理方法&#xff0c;旨在确保员工的工作与组织的目标保持一致&#xff0c;以及激励和提高员工的工作表现。它涉及设定明确的目标和标准&…