重生c++系列之类与对象(中篇)

news2025/1/19 17:23:08

好的继上期,我们今天带来c++类与对象系列的继续学习。

类的6个默认成员函数

如果一个类中什么成员都没有,简称为空类。
空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员
函数。
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

 六个默认成员函数会实现6个功能,我们先来看第一个:

构造函数

上一段代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Printf()
	{
		cout << _year << '/' << _month << '/' << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	d1.Printf();
	
	return 0;
}

大家在刚开始写代码的时候,比如说这里写了一个date类,我们忘记了初始化,直接调用printf函数,由于没有初始化,三个变量的值就会使三个随机数:

 这是不是就麻烦了,这个事情不仅仅是我们经常忘记,c++祖师爷也经常忘记,所以祖师爷就想给c++多个功能,自己初始化,所以祖师爷就搞了一个新的东西:构造函数

构造函数的特性:

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

其特征如下:
1. 函数名与类名相同。
2. 无返回值。 (不需要写void)
3. 对象实例化时编译器自动调用对应的构造函数。
4. 构造函数可以重载。 (可以有多个构造函数,多种初始化方式)
我们给上边日期类写一个构造函数试试,看看是否真的可以初始化:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:
    //构造函数
	Date()
	{
		_year = 2023;
		_month = 8;
		_day = 30;
		cout << "构造函数" << endl;
	}
	void Printf()
	{
		cout << _year << '/' << _month << '/' << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	d1.Printf();
	
	return 0;
}

很好,我们可以发现该类就是调用了我们写构造函数进行了初始化日期,并且我们无需调用,省下了很多时间
现在的初始化没有传参,是规定好的,就是我们构建无数次日期类的变量,都是这个时间,如果我们想自己在外部(主函数)自己传一个日期进行初始化,该怎么办?
非常简单,再写一个带参的构造函数就可以了
上代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:
    //构造函数
	Date()
	{
		_year = 2023;
		_month = 8;
		_day = 30;
		cout << "构造函数" << endl;
	}
    //有参构造函数
	Date(int year,int month,int day)
	{
		_year = year;
		_month = month;
		_day = day;
		cout << "有参构造函数" << endl;
	}
	void Printf()
	{
		cout << _year << '/' << _month << '/' << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	d1.Printf();
	Date d2(2023,1,1);
	d2.Printf();
	return 0;
}

可以看到,我们另外又写了一个构造函数,是有参数的构造函数,两个构造函数构成函数重载,如果我们没传参,就自动调用第一个无参构造函数,如果又传参,就按照我们自己的想法调用第二个有参构造函数进行初始化,是不是和我们前两期函数重载的知识点结合起来了!另外,大家要注意一下d2的传参数方式,很新很方便,比调用init要方便的多,这个大家记住就行了。
运行截图论证:
再回想一下我们第二期讲的内容,是不是有一个缺省参数,好好好,那么是不是可以根据这个知识点把两个构造参数合二为一
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:

	Date(int year=2023,int month=8,int day=30)
	{
		_year = year;
		_month = month;
		_day = day;
		cout << "有参构造函数" << endl;
	}
	void Printf()
	{
		cout << _year << '/' << _month << '/' << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	d1.Printf();
	Date d2(2023,1,1);
	d2.Printf();
	return 0;
}

 这样不单单就简介了,还更灵活了,看不懂的同学自觉去看前两期内容

两次都是调用的这个构造函数,这就是缺省的魅力

是不是还可以这么玩:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:

	Date(int year=2023,int month=8,int day=30)
	{
		_year = year;
		_month = month;
		_day = day;
		cout << "有参构造函数" << endl;
	}
	void Printf()
	{
		cout << _year << '/' << _month << '/' << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	d1.Printf();
	Date d2(2023,1,1);
	d2.Printf();
	Date d3(2023);
	d3.Printf();
	Date d4(2023, 12);
	d4.Printf();
	return 0;
}

好好好,缺省参数被我们玩坏了

刚刚我们看的是日期类的构造函数,相对来说比较简单,现在我们来进阶一下,看看栈的构造函数:
class Stack
{
public:
    Stack()
    {
        a = nullptr;
        top = capacity = 0;
    }
    void Push(int x)
    {
        if (top == capacity)
        {
            cout << capacity << "扩容" << endl;
            size_t newcapacity = capacity == 0 ? 4:capacity * 2;
            a = (int*)realloc(a, sizeof(int) * newcapacity);
            capacity = newcapacity;
        }
        a[top++] = x;
    }
private:
    int *a;
    int top;
    int capacity;
    int size;
};
众所周知,构造函数也叫默认成员函数,默认什么意思,也就是说,我们不去写构造函数,编译器也会自动生成
构造函数,也是默认成员函数,我们不写,编译器会自动生成编译生成的默认构造的特点:
1、我们不写才会生成,我们写了就不会生成了
2、内置类型的成员不会处理
3、自定义类型的成员才会处理,会去调用这个成员的构造函数
像是我们之前写的那个日期类,如果我们没写构造函数,他也不会自动生成,因为三个成员都是内置类型
一般情况下,都需要我们自己写构造函数,如果类变量都是自定义变量,可以有选择不写

析构函数

通过前面构造函数的学习,我们知道一个对象是怎么来的,那一个对象又是怎么没呢的? 析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由 编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

析构函数的特性

析构函数是特殊的成员函数,其特征如下:

1. 析构函数名是在类名前加上字符 ~。

2. 无参数无返回值类型。

3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构 函数不能重载

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

上代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:

	Date(int year = 2023, int month = 8, int day = 30)
	{
		_year = year;
		_month = month;
		_day = day;
		cout << "有参构造函数" << endl;
	}
	void Printf()
	{
		cout << _year << '/' << _month << '/' << _day << endl;
	}
	~Date()
	{
		cout << "析构函数" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	d1.Printf();
	Date d2(2023, 1, 1);
	d2.Printf();
	return 0;
}

看,我们写的析构函数自动被调用了 

日期类的析构函数我们看不出什么来,来试试栈的析构函数

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Stack
{
public:
	Stack()
	{
		cout << "构造函数" << endl;
		a = nullptr;
		top = capacity = 0;
	}
	void Push(int x)
	{
		if (top == capacity)
		{
			cout << capacity << "扩容" << endl;
			size_t newcapacity = capacity == 0 ? 4 : capacity * 2;
			a = (int*)realloc(a, sizeof(int) * newcapacity);
			capacity = newcapacity;
		}
		a[top++] = x;
	}
	~Stack()
	{
		cout << "析构函数" << endl;
		free(a);
		a = nullptr;
		top = capacity = 0;
	}
private:
	int* a;
	int top;
	int capacity;
	int size;
};
int main()
{
	Stack s1;

	return 0;
}

自动调用,无论写多少个,所以这就生下了我们很多初始化和删除的时间

拷贝构造函数

上代码:

#include<iostream>
using namespace std
class Date
{
public:
 Date(int year = 1900, int month = 1, int day = 1)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 // Date(const Date& d)   // 正确写法
    Date(const Date& d)   // 错误写法:编译报错,会引发无穷递归
 {
 _year = d._year;
 _month = d._month;
 _day = d._day;
 }
private:
 int _year;
 int _month;
 int _day;
};
int main()
{
 Date d1;
 Date d2(d1);
 return 0;
}

直接拷贝的d1给d2赋值

拷贝构造的特征:

拷贝构造函数也是特殊的成员函数,其特征如下:
1. 拷贝构造函数是构造函数的一个重载形式
2. 拷贝构造函数的参数只有一个必须是类类型对象的引用,使用传值方式编译器直接报错
因为会引发无穷递归调用。

赋值运算符重载

上代码:

#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:

	Date(int year = 2023, int month = 8, int day = 30)
	{
		_year = year;
		_month = month;
		_day = day;
		cout << "有参构造函数" << endl;
	}
	void Printf()
	{
		cout << _year << '/' << _month << '/' << _day << endl;
	}
	~Date()
	{
		cout << "析构函数" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2023, 1, 1);
	Date d2(2023, 1, 2);

	d1 < d2;
	return 0;
}

大家看看这样比较两个日期类的变量行不行

答案是当然不行

 但是这样写

int i,j;

i<j;

这样比较,就可以,这是因为编译器知道int变量的存储模式和比较方式

那么我们可不可以让编译器也可以直接比较date

这就涉及到这个课题内容运算符重载

#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:

	Date(int year = 2023, int month = 8, int day = 30)
	{
		_year = year;
		_month = month;
		_day = day;
		cout << "有参构造函数" << endl;
	}
	void Printf()
	{
		cout << _year << '/' << _month << '/' << _day << endl;
	}
	~Date()
	{
		cout << "析构函数" << endl;
	}

//private:
	int _year;
	int _month;
	int _day;
};
bool operator<(const Date& x1, const Date& x2)
{
	if (x1._year < x2._year)
	{
		return true;
	}
	else if (x1._year == x2._year && x1._month < x2._month)
	{
		return true;
	}
	else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day)
	{
		return true;
	}
	else
	{
		return false;
	}
}
int main()
{
	Date d1(2023, 1, 1);
	Date d2(2023, 1, 2);

	cout<<(d1 < d2)<<endl;
	return 0;
}

有函数重载,就有运算符重载,上边就是使用规则,大家记住就行了

那有<运算符,就有<=,>,>=,=,+,-,+=,-=

博主在这里一一实现了

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}

	
	bool operator<(const Date& d)
	{
		if (_year < d._year)
		{
			return true;
		}
		else if (_year == d._year && _month < d._month)
		{
			return true;
		}
		else if (_year == d._year && _month == d._month && _day < d._day)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	bool operator==(const Date& d)
	{
		return _year == d._year
			&& _month == d._month
			&& _day == d._day;
	}

	// d1 <= d2
	bool operator<=(const Date& d)
	{
		return *this < d || *this == d;
	}

	bool operator>(const Date& d)
	{
		return !(*this <= d);
	}

	bool operator>=(const Date& d)
	{
		return !(*this < d);
	}

	bool operator!=(const Date& d)
	{
		return !(*this == d);
	}

	int GetMonthDay(int year, int month)
	{
		int monthArray[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

		if (month == 2 
			&& ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
		{
			return 29;
		}

		return monthArray[month];
	}

	Date& operator+=(int day)
	{
		_day += day;
		while (_day > GetMonthDay(_year, _month))
		{
			// 月进位
			_day -= GetMonthDay(_year, _month);
			++_month;

			// 月满了
			if (_month == 13)
			{
				++_year;
				_month = 1;
			}
		}

		return *this;
	}

	Date operator+(int day)
	{
		Date tmp(*this);
		tmp += day;
		return tmp;

		//tmp._day += day;
		//while (tmp._day > GetMonthDay(tmp._year, tmp._month))
		//{
		//	// 月进位
		//	tmp._day -= GetMonthDay(tmp._year, tmp._month);
		//	++_month;

		//	// 月满了
		//	if (tmp._month == 13)
		//	{
		//		++tmp._year;
		//		tmp._month = 1;
		//	}
		//}
		//return tmp;
	}

private:
	// 内置类型
	int _year;
	int _month;
	int _day;
};

 

关于日期各种加减乘除运算的逻辑实现是没有任何难度的,就是年完月,月完日,进位加

大家要理解的是重载的操作

注意:
不能通过连接其他符号来创建新的操作符:比如operator@
重载操作符必须有一个类类型参数
用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐
藏的this
.* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出
现。

好的类和对象的中篇就到这里,对于默认成岩函数基本就接好玩了,期待三连

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

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

相关文章

【Latex】使用技能站:(一)Visio导出矢量图并导入Latex模板

Visio导出矢量图并导入Latex模板 引言1 安装Inkscape工具1.1 官网下载并安装1.2 添加环境变量 2 Visio导出svg文件3 Inkscape 转换为PDF或EPS格式4 Latex导入.pdf或者.eps矢量图 引言 矢量图格式有&#xff1a;svg&#xff0c;eps&#xff0c;pdfVisio能导出的矢量图格式有&am…

信息技术如何监测UPS设备?看了这篇我明白了

UPS设备能够在电力中断或波动的情况下提供临时的电力支持&#xff0c;以保障办公设备的正常运行和数据的安全性。 然而&#xff0c;仅仅拥有UPS设备是不够的&#xff0c;实时监控和管理这些设备同样至关重要。通过对办公室UPS进行监控&#xff0c;我们可以及时发现电力问题、设…

哪个牌子的运动耳机比较好、运动耳机品牌推荐

跑步和健身的朋友通常喜欢戴着运动耳机&#xff0c;这样可以在运动时享受音乐的伴奏。这不仅提高了运动效果&#xff0c;还能减轻疲劳感。因此市场上涌现了许多高品质的运动耳机品牌。现在让我们来看看一些受到健身达人们推荐的耳机款式吧&#xff01; 1、南卡骨传导耳机 近年…

windows使用远程桌面连接

第一步&#xff1a;winR进入命令控制 第二步&#xff1a;输入mstsc进入远程连接界面 第三步&#xff1a;输入需要远程控制的加算器ip 注意&#xff1a; 控制计算器与被控制计算器都需要满足以下条件 可以使用远程桌面连接到 Windows 10 专业版和企业版、Windows 8.1 和 8 企…

GitLab启动失败:fail: alertmanager: runsv not running

问题描述 sudo gitlab-ctl restart &#xff0c;报错如下 &#xff1a; summergaoubuntu:/etc/gitlab$ sudo gitlab-ctl start fail: alertmanager: runsv not running fail: gitaly: runsv not running fail: gitlab-exporter: runsv not running fail: gitlab-workhorse: …

docker desktop安装es 并连接elasticsearch-head:5

首先要保证docker安装成功&#xff0c;打开cmd&#xff0c;输入docker -v&#xff0c;出现如下界面说明安装成功了 下面开始安装es 第一步&#xff1a;拉取es镜像 docker pull elasticsearch:7.6.2第二步&#xff1a;运行容器 docker run -d --namees7 --restartalways -p 9…

MySQL日期格式及日期函数实践

目录 日期格式 日期函数 CURDATE()和CURRENT_DATE()CURTIME()和CURRENT_TIME()NOW()和CURRENT_TIMESTAMP()DATE_FORMAT()DATE_ADD()和DATE_SUB()DATEDIFF()DATE()DAYNAME()和MONTHNAME() 1. 日期格式 在MySQL中&#xff0c;日期可以使用多种格式进行存储和表示。常见的日期格式…

【大数据之Kafka】五、Kafka生产者之生产经验

1 生产者如何提高吞吐量 由于linger.ms默认为0&#xff0c;即缓冲区队列中一有数据就sender线程就将其拉出到Kafka集群&#xff0c;效率比较低&#xff0c;提高生产者吞吐量有四种方式&#xff1a; &#xff08;1&#xff09;扩大批次的大小batch.size&#xff0c;默认为16k&a…

LeetCode-160. 相交链表

这是一道真的非常巧妙的题&#xff0c;题解思路如下&#xff1a; 如果让他们尾端队齐&#xff0c;那么从后面遍历就会很快找到第一个相交的点。但是逆序很麻烦。 于是有一个巧妙的思路诞生了&#xff0c;如果让短的先走完自己的再走长的&#xff0c;长的走完走短的&#xff0c;…

达梦数据库屏蔽关键字

一、配制位置 配置设备&#xff1a;需要连接数据库的程序运行的设备&#xff1b; 配置位置&#xff1a; 1.32 位的 DM 安装在 Win32 操作平台下&#xff0c;此文件位于%SystemRoot%\system32 目录&#xff1b; 2.64 位的 DM 安装在 Win64 操作平台下&#xff0c;此文件位于…

在线制作作息时间表

时光荏苒&#xff0c;岁月如梭&#xff0c;人们描述时光易逝的句子&#xff0c;多如星河。 一寸光阴一寸金&#xff0c;寸金难买寸光阴。 人生就是一段时间而已&#xff0c;所以我明白了一个道理 人生之中最大的浪费就是时间的浪费 因此我想我们教给我们孩子重要的一课应该也是…

PMO的秘密武器:项目评估与审计机制的深度解析

引言&#xff1a;PMO的核心价值与挑战 项目管理办公室&#xff08;PMO&#xff09;在组织中的角色日益重要。它是组织中的一个关键部门&#xff0c;负责确保项目的顺利进行&#xff0c;并确保项目与组织的整体战略保持一致。但与此同时&#xff0c;PMO也面临着许多挑战。其中最…

1.6 编写双管道ShellCode

本文将介绍如何将CMD绑定到双向管道上&#xff0c;这是一种常用的黑客反弹技巧&#xff0c;可以让用户在命令行界面下与其他程序进行交互&#xff0c;我们将从创建管道、启动进程、传输数据等方面对这个功能进行详细讲解。此外&#xff0c;本文还将通过使用汇编语言一步步来实现…

【WINAPI】文件读写操作问题

问题描述 在利用WINAPI中的WriteFile和ReadFile函数进行文件读写操作时&#xff0c;出现无法正常读写文件报错。 分析问题 查阅WINAPI源码&#xff0c;查看参数列表各个参数的数据类型。 发现其中第二个参数&#xff0c;也就是需要写进文件的真实数据&#xff0c;其数据类型…

Google Play商店优化排名因素之应用的评分评论

下载次数是应用程序受欢迎程度的指标&#xff0c;Google在对我们的应用程序进行排名时也会将其考虑在内。评级和评论会影响应用程序的转化率&#xff0c;因为许多用户在做出决定之前会根据平均评级或最近的评论来评估我们的应用程序。 1、评级的重要性。 如果我们的应用程序有…

安卓版yolo-fastest

安卓版本yolofastest效果测试 安卓配置OPENCV4ANDROID&#xff0c;见我的博客一篇文章opencv4dandroid配置 这个不需要使用JNI&#xff0c;十分简单的配置 说真的&#xff0c;其实只调用OPENCV的函数&#xff0c;自己写的代码不多&#xff0c;使用OPENCV4ANDROID和JNI的时间差…

im6ull-uboot(2021.07)移植(一)

文章目录 声明1 获取源码1.1 从u-boot官网获取1.2 从芯片厂商获取1.3 从开发板厂商获取 2 修改顶层Makefile3 xxx_defconfig配置文件3.1 拷贝生成自己的配置文件3.2 修改defconfig文件3.2.1 查看defconfig文件3.2.2 修改defconfig文件 3.3 添加其他配置文件3.3.1 添加配置头文件…

深入了解电商API的历史和业务场景

在数字时代&#xff0c;电子商务的兴起改变了传统的商业模式&#xff0c;使得商品和服务的交易变得更加便捷和高效。作为电子商务的重要组成部分&#xff0c;电商API&#xff08;应用程序编程接口&#xff09;在过去的几十年中得到了广泛的应用和不断发展。本文将深入探讨电商A…

css 分割线中间带文字

效果图 代码块&#xff08;自适应&#xff09; <div class"line"><span class"text">我是文字</span></div>.line{height:0;border-top:1px solid #000;text-align:center;}.text{position:relative;top:-14px;background-color:#…