C++----- 构造函数 | 析构函数 | 拷贝构造函数 | 运算符重载 | const关键字 | 友元函数


一、构造函数

构造函数为默认成员函数之一,不写编译器会自动生成
他有如下特征:
① . 函数名和类名相同
② . 无返回值
③ . 对象实例化时自动调用对应构造函数
④ . 构造函数可以重载

有了构造就可以省去一直Init的步骤,我们看下面代码

class Date {
	//注: 1和3语法上可以同时存在,但调用时会报错
	//	  1和2可合并为3
public:
	//1
	//Date()
	//{
	//	_year = 1;
	//	_month = 1;
	//	_day = 1;
	//}
	2
	//Date(int year, int month, int day)
	//{
	//	_year = year;
	//	_month = month;
	//	_day = day;
	//}
	//3
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "--" << _month << "--" << _day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	d1.Print();
	cout << endl;
	Date d3(2023,11,11);
	d3.Print();
	return 0;
}

在这里插入图片描述

可以看见,我们不传值时,会根据全缺省参数初始化,如果自己传值的话,那就会初始化传的值,但如果没有构造函数,编译器就会自动生成,这里我们就要提一下编译生成的默认构造特点
1.不写才会生成,写了不会生成
2.内置类型的成员不会处理
3.自定义类型的成员会去调用这个成员的构造函数
结果如下

class Date {

public:

	void Print()
	{
		cout << _year << "--" << _month << "--" << _day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	d1.Print();
	cout << endl;

	return 0;
}

在这里插入图片描述

但这生成的是个什么鬼,和没处理又有什么区别哇
总结一下:一般情况下都要写构造函数,除非成员变量全是自定义类型,这里就不得不提一下太子爷,两栈实现的队列了

class Myqueue {
	Stack _pushst;
	Stack _popst;
};

这玩意你让他不写构造函数我一点意见没有

关于构造函数,这里再提一下默认构造函数:无参,全缺省,系统自动生成的都叫默认构造函数(只要不传参,就可以调用的),且默认构造函数只有一个


二、析构函数

对象在销毁时会调用析构函数,完成对象中资源清理工作,同样的,他也是个默认成员函数(类似destroy)
特征:
1.析构函数名在类前加~
2.无参,无返回值
3.一个类只有一个析构函数,不能重载,若无定义,则会自动生成
4.对象生命周期结束时,C++编译器系统自动调用析构函数

class Date {
public:
	Date(int year = 2023, int month = 11, int day = 11)//默认构造函数
	{
		_year = year;
		_month = month;
		_day = day;
	}

	~Date()//析构
	{
		cout << "~Date()" << endl;
		_year = 1;
		_month = 1;
		_day = 1;
	}

	void Print()
	{
		cout << _year << "--" << _month << "--" << _day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	d1.Print();
	cout << endl;
	return 0;
}

在这里插入图片描述

从这里我们可以清楚看见调用了析构函数,但日期类内部成员函数都是内置类型,当我们写像栈那样的复杂类,需要手写析构函数,这里我们编译一下,在仔细看一下是否调用了析构函数

![在这里插入图
默认析构函数的特性和默认构造相似
1.内置类型成员不会处理
2.自定义类型调用它的析构函数


三、拷贝构造函数

class Date {
	
};

class Stack {

};

void func1(Date d){ }

void func2(Stack s){ }

int main()
{
	Date d1;
	func1(d1);
	Stack s1;
	func2(s1);//这里会崩溃
	return 0;
}

为什么会崩溃呢
因为func2函数中_a在堆上申请的空间和main函数中_a申请的空间是一个,func2函数结束时,会进行一次析构,而main函数结束同样也要进行一次析构,从而一块空间进行了两次析构,所以就会崩溃

这里func2参数可以用引用,一个对象别名不会析构,但怎么样才能让s改变不影响s1呢,这里就映出了拷贝构造函数
特征:
1.拷贝构造函数是构造函数的一个重载形式
2.拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值调用会让编译器直接报错,因为会引发无穷调用

class Date {
public:
	Date(int year = 2023, int month = 11, int day = 11)
	{
		_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;
	d1.print();

	Date d2(d1);
	d2.print();

	return 0;
}

在这里插入图片描述

默认生成的拷贝构造函数
我们不写,默认生成的拷贝构造和之前构造函数特性不一样
1.内置类型:值拷贝
2.自定义类型:调用他自己的拷贝构造

总结:Date不需要我们实现拷贝构造,默认生成就可以用
Stack需要我们自己实现深拷贝的默认构造,默认生成的会出现问题(可能会析构两次)

注:对于两栈实现队列这一题,所有的默认成员函数都可以默认生成(全是自定义类型对象)

四、运算符重载

在这里插入图片描述
总所周知,两个自定义类类型是不可以直接比较,但就是想让他直接比,那怎么办呢?,换句话说,怎么样才能让这种写法合法,而你:运算符重载,你是我的英雄
C++规定:operator加运算符为函数名,我们后面进行详细讲解

*特性:
1.C++为了增强代码得可读性,引入了运算符重载,运算符重载是具有特殊函数名得函数,其余和普通函数相似
2.函数名为operator后面接需要重载的运算符
3.函数样式:返回值类型 operator运算符(参数列表)
注:
1.不能通过连接其他符号来创建新的操作符比如:operator@
2.重载操作符必须有一个类类型参数
3.用于内置类型的运算符,其含义不能改变,例如内置的‘+’,不能改变其含义
4.作为类成员函数重载时,其形参看起来比操作数少一,因为成员函数的第一个参数为隐藏的this指针

5. .
:: sizeof ?: . 这五个运算符不能重载
在这里插入图片描述

关于运算符重载还有个需要注意的,那便是赋值运算符重载,很多人会把它和拷贝构造函数搞混,这里来详细分析一下

赋值运算符重载:两个已经存在的对象进行拷贝
拷贝构造:一个已经存在的对象去初始化另一个对象

class Date {
public:
	Date(int year = 2023, int month = 11, int day = 11)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void print()
	{
		cout << _year << "--" << _month << "--" << _day << endl;
	}
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			this->_year = d._year;
			this->_month = d._month;
			this->_day = d._day;
		}
		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(1, 1, 1);
	Date d2(d1);//拷贝构造
	Date d3(2023, 11, 12);
	d1 = d3;//赋值运算符
	d1.print();
	d2.print();
	d3.print();
	return 0;
}

运行结果:
在这里插入图片描述
这里运算符重载就先讲到这里,更多关于运算符重载的实现,可以参见博主的日期类实现文章:日期类实现

五、const关键字

const这个关键字我们已经了解很多,下面再带着例子略带提一下

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

这是我们上面提到的<运算符重载,这里再函数名后加了个const,从而达到禁止修改的目的
注:
const变量不能调用非const成员函数
非const变量可以调用const成员函数
const成员函数不能调用非const成员变量
非const成员函数可以调用const成员变量
(这里涉及的是权限的放大与缩小)

总结:只读函数可以加const,内部不涉及修改生成

六、取地址运算符重载

Date* Date::operator&( )
{
	return this}

平时不需要自己写,因为库里有,所有我们平时可以直接用自动生成的,除非你不想让别人取到地址,比如:return nullptr;

七、友元函数

细心观察的小伙伴们都会发现,前面打印都用的print函数,那么我们想用流提取操作符打印可以吗?可以,但是有缺陷

在这里插入图片描述
这里我们直接使用会报错,那么我们对<<进行重载试试
在这里插入图片描述
打印出来了没错,但是这个d1<<cout看上去是不是蜘蛛侠骑马—马拉个彼得,还得反着写,为什么呢,因为类内this指针永远是第一个参数,所有我们要写成全局函数,但成员函数私有,那这时友元就出来了

在这里插入图片描述
这个时候,是不是就好受多了,说到流插入/提取,我们为什么不用scanf和printf呢,因为这两个函数不好支持自定义类型,所以才引入了流插入/提取
但是这个时候问题又出现了
在这里插入图片描述
当我想连续输入时,他却报错了,一个一个写那得多少行,分析一下,cout<<d1返回的是空类型,所以会出问题也正常,这时我们把void换成ostream&,就可以完美解决了

class Date {
public:
	Date(int year = 2023, int month = 11, int day = 11)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void print()
	{
		cout << _year << "--" << _month << "--" << _day << endl;
	}
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	friend ostream& operator<<(ostream& out,const Date& d);
private:
	int _year;
	int _month;
	int _day;
};
ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "--" << d._month << "--" << d._day << endl;
	return out;
}
int main()
{
	Date d1(2023, 11, 12);
	Date d2(2023, 11, 13);
	cout << d1 << d2;
	return 0;
}

运行结果:
在这里插入图片描述

友元函数特性
1.友元函数可访问类的私有和保护成员,但不是类的成员函数
2.友元函数不能用const修饰
3.友元函数可以在类定义的任何地方声明,不受类访问限定符限制
4.一个函数可以是多个类的友元
5.友元函数的调用与普通函数原理相同

友元类

class Date {
private:
	Time _t;
};

class Time {
	friend class Date;
};

这里声明Date类是Time类的友元类,则在日期类中就能直接调用时间类中的私有成员
但是Time类不能调用Date类私有成员

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值