类中默认的函数

本文介绍了C++中类的四种默认函数:构造函数、析构函数、拷贝构造函数和赋值运算符的重载函数。阐述了它们的作用、使用场景以及注意事项,如构造函数用于初始化成员变量,析构函数负责释放资源,拷贝构造函数处理对象复制,而赋值运算符重载处理对象赋值。文章还讨论了构造函数和析构函数的特性,如构造函数不可重载,析构函数可以人为调用但需谨慎。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

类中默认的函数

在c++中,我们知道类中默认的函数总共有6个,它们分别是:
1.构造函数
2.析构函数
3.拷贝构造函数
4.赋值运算符的重载函数
5.取地址操作符的重载函数
6.const修饰的取地址操作符的重载函数
因为笔者的知识储备量还不够,所以只是简绍一下前面四个默认的函数,希望能对大家有所帮助。
首先,什么是默认的函数呢?简单来说就是类中自带的函数,假如我们没有在类中写这些函数,当我们在调用点调用到这些函数的方法时,系统就会拿出自带的函数来进行处理。但是在在有些情况下,当系统提供的函数不能满足我们的功能时,就会出错。

构造函数

我们知道在一个类中,会有一些成员变量。它们的默认值是cccc cccc,我们当然不可能拿这些默认值来进行操作。所以我们就用构造函数来对这些成员变量进行初始化。可以通过一个商品类代码代码来了解:
1.构造函数的返回值类型就是类名称
2.先从对上开辟一块(name+1)大小的空间赋给mname(确保空间足够)
3.将name里面的字符串拷贝到新开辟的空间mname中
4.将num赋值给mnum,price赋值给mprice
这样,一个商品类的构造函数就写好了。

class commodity
{
public:
	commodity(char* name,int num,float price)
	{
		mname = new char[strlen(name)+1]();
		strcpy_s(mname,strlen(name)+1,name);
		mnum = num;
		mprice = price;
	}
private:
	char* mname;//商品名称
	int mnum;//商品数量
	float mprice;//商品价格
};
析构函数

析构函数,相当于c语言中的free,它会将堆内存释放并指向空,将栈上开辟的变量置为0。

class commodity
{
public:
	~commodity()
	{
		delete[] mname;
		mname = NULL;
		mprice = 0;
		mnum = 0;
	}
private:
	char* mname;//商品名称
	int mnum;//商品数量
	float mprice;//商品价格
}

思考:构造函数和析构函数可以在一个类中定义多个吗?
我们将类中的构造函数和析构函数可以比喻为人的出生和死亡:人的出生不同决定了他的命运
,我们称其为“生而不同”;但是人的死亡都是一样的,除了21克灵魂,将一无所有。所以回到计算机世界,我们知道构造函数是可以重载的,但是析构不能重载。
思考二:构造函数和析构函数可以人为的调动吗?
回答这个问题,我可以逆推,假设这两个函数都是可以人为调动的。我们知道构造函数是为了初始化对象所在的空间,而对象又依赖构造函数进行初始化,这样就会形成一个类似于死锁的概念。那么我们知道,构造函数时不可以人为调动的。那析构呢?假设我们人为的调动了析构函数,那第一次调动析构函数时,我们就释放了对象中的堆空间,但是当return结束后,系统还会调用析构函数来释放堆空间的,这时系统就出错了。所以,析构函数是可以人为调动的,但是一般不要去调动,可能会引发错误。

拷贝构造函数

假设我们在调用点生成了c1和c2两个对象,将c1初始化并将c1赋值给c2:
commodity c1(“牛奶”, 3.5, 100);
commodity c2 = c1;
首先让我们看看默认的函数是怎么处理的:

这个错误非常明显,因为里面有一个指针。这时两个指针就会指向同一块内存空间,那按照我们出入栈的顺序来看,c2先出栈调动析构函数。这时c1在出栈时,就没有堆内存可以释放了。这时程序出错。

class commodity
{
public:
	commodity(const commodity& rhs)
	{
		mname = rhs.mname;
		//指针类型赋值:两个指针指向相同的内存块
		mprice = rhs.mprice;
		mnum = rhs.mnum;
	}
private:
	char* mname;//商品名称
	int mnum;//商品数量
	float mprice;//商品价格
}

那接下来看一下正确的拷贝构造函数:

class commodity
{
public:
	commodity(const commodity& rhs)
	{
		mname = new char[strlen(rhs.mname) + 1]();
		strcpy_s(mname, strlen(rhs.mname)+1, rhs.mname);
		mprice = rhs.mprice;
		mnum = rhs.mnum;
	}
private:
	char* mname;//商品名称
	int mnum;//商品数量
	float mprice;//商品价格
}
int main()
{
	commodity c1("牛奶", 3.5, 100);
	commodity c2 = c1;
	//如果不提供拷贝构造函数,运行将会出错原因:
	//c1和c2将指向同一个堆内存(c1),由于入栈的原因c2将先进行析构
	//此时就将c1指向的堆内存也一起释放,到c1进行析构时,程序出错
	return 0;
}

思考三:如果把const commodity& rhs中的&去掉会怎样?
拷贝构造函数实际上是把实参传给形参:(const commodity rhs = c1);如果把&去掉,你就会发现这又是一个拷贝构造函数;那我们再来调动拷贝构造函数来处理这个表达式,结果是调用的拷贝构造函数依旧是(const commodity& rhs),那这样就形成了一个递归的过程,最终会导致栈溢出从而出错

总结:
1.拷贝构造函数就是用一个已存在的对象来生成相同类型的新对象
2.rhs接受的是c1对象,this指针接受的是c2这个对象。
3.当成员变量有指针类型时,我们就要不能使用系统给出的拷贝构造函数了,而因该自己写一个
4.拷贝构造函数的形参一定要是引用,否则就会出现递归拷贝构造

赋值运算符的重载函数

赋值运算符的重载函数中我们需要用operator函数来做处理,看这样一个代码:
commodity c1(“牛奶”, 3.5, 100);
commodity c2(“面包”, 4.5, 100);
c1 = c2;
这个可不是拷贝构造函数,我们知道拷贝构造函数是拿一个已存在的对象来生成相同类型的新对象。而赋值运算符的重载使用一个已存在的对象赋值给相同类型的已存在对象
在代码中的实现如下:
它是一个返回值为类引用的函数,并且它如果自己给自己赋值的话,将会毫无意义。
它的流程如下:
1.判断是否自赋值
2.释放掉旧的堆空间
3.开辟新的空间
4.将要赋值的内容拷贝到被赋值的对象中

class commodity
{
public:
	commodity& operator=(const commodity& rhs)
	{
		if (this == &rhs)
		{
			return;
		}
		delete[] mname;
		mname = new char[strlen(rhs.mname) + 1];
		strcpy_s(mname, strlen(rhs.mname) + 1, rhs.mname);
		mprice = rhs.mprice;
		mnum = rhs.mnum;
	}
private:
	char* mname;//商品名称
	int mnum;//商品数量
	float mprice;//商品价格
}

最后,让我们将这四个函数放在一起,看看它的最终面目吧。

#include <iostream>
class commodity
{
public:
	commodity(const char* name, float price, int num)
	{
		mname = new char[strlen(name) + 1]();
		strcpy_s(mname, strlen(name) + 1, name);
		mprice = price;
		mnum = num;
	}
	commodity(const commodity& rhs)
	{
		mname = new char[strlen(rhs.mname) + 1]();
		strcpy_s(mname, strlen(rhs.mname)+1, rhs.mname);
		mprice = rhs.mprice;
		mnum = rhs.mnum;
	}
	commodity& operator=(const commodity& rhs)
	{
		if (this == &rhs)
		{
			return;
		}
		delete[] mname;
		mname = new char[strlen(rhs.mname) + 1];
		strcpy_s(mname, strlen(rhs.mname) + 1, rhs.mname);
		mprice = rhs.mprice;
		mnum = rhs.mnum;
	}
	~commodity()
	{
		delete[] mname;
		mname = NULL;
	}
private:
	char* mname;
	float mprice;
	int mnum;
};

int main()
{
	commodity c1("牛奶", 3.5, 100);
	commodity c3 = c1;
	commodity c2("面包", 4.5, 100);
	c1 = c2;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值