类和对象——中(2)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

本篇文章接上篇文章类和对象——中-CSDN博客如果类和对象上没有看,这里的链接是类和对象上的链接:类和对象——上-CSDN博客构造函数是和类同名的函数,没有返回值,并且构造函数虽然是叫做构造函数,但它的主要任务并不是开空间创对象,而是初始化对象。class Apublic:A() //构造函数Acout << "构造函数" << endl;return0;构造函数和普通的函数其实并没有多大区别,只有一个区别,那就是没有返回值。https://blog.csdn.net/2301_76643854/article/details/134159666?spm=1001.2014.3001.5502

 接下来了解下类中的其他几类构造函数


回顾:

        我们先回顾下,上篇文章讲的内容有些什么,在类中有六个默认构造函数,意思就是我们不写,编译器也会默认生成,当然这必须是我们不写,编译器才会生成,我们写了的话,编译器会直接调用我们所写的默认构造函数,关于是否自己写默认构造函数,看编译器所生成的默认构造函数是否符合我们的要求。

构造函数:无返回值,函数名与类名相同,对象实例化的时候自动调用。

默认构造函数:起初始化作用,在程序运行时自动调用。

析构函数:起销毁作用,在程序结束的时候自动调用。

this指针:在类,对象中的隐式参数,this指针并不存放在对象中,this指针是一个形参,在类中并不显示写。

类中的成员函数并不是存在类中的,也并不是存在对象中的。

正文:

一、拷贝构造函数

拷贝构造函数只有一个形参,这个形参是对本类型对象的引用(为右值,具有常性,一般使用const修饰),在使用已经存在的对象创建新的对象的时候由编译器自动调用。对于自定义类型,调用它的拷贝构造,对于内置类型,进行值拷贝。

拷贝构造函数的特征:

1、拷贝构造函数是构造函数的一个重载形式。

2、拷贝构造函数的参数只能有一个,且必须是类对象的引用,使用传值的方法会编译器会直接报错,因为会发生无穷递归调用。

拷贝构造的使用:

        在我们写代码中拷贝构造是不可避免的,比如说我们在给函数传参的时候,这时候我们传的是对象本身吗?并不是,这里是调用了拷贝构造函数对s1进行了拷贝,再将拷贝的类交给Funk函数,所以说,拷贝构造是我们避不开的函数。

class  A
{
static int a;
};
 void Funk(A a){}

int main()
{
	A s1;
	Funk(s1);
}

 当然拷贝构造函数还有一个坑,大家来看这段程序:

         这里的问题就涉及到了析构函数,我们都知道析构函数会在程序结束的时候自动调用,但是这里我们所写的析构函数调用的时候会将b的空间给释放掉,但是我们这里使用了拷贝构造,将s1的b拷贝给了s2的b,这时两个对象的b都指向同一块空间,但是析构函数是每有一个对象就会调用一次,所以这里就出现了两次析构的问题。

class  A
{
	int a=1;
	int* b = (int*)malloc(sizeof(int)*1);

public:
	void Print()
	{
		cout << a << endl;
	}
	~A()
	{
		free(b);
		cout << "~A" << endl;
	}
};
 void Funk(A a)
 {
	 a.Print();
 }
	
int main()
{
	A s1;
	A s2(s1);
	return 0;
}

 好了,我们来看看为什么说,写拷贝构造函数必须使用引用作为参数。

来看看:

class  A
{
	int a;
	int* b;

public:
	void Print()
	{
		cout << a << endl;
	}
	~A()
	{
		free(b);
		cout << "~A" << endl;
	}
	A()
	{
		int a = 1;
		int* b = (int*)malloc(sizeof(int) * 1);
	}
	A(A& s)
	{
		a = s.a;
		b = s.b;
	}
};
 void Funk(A a)
 {
	 a.Print();
 }
	
int main()
{
	A s1;
	A s2(s1);
	return 0;
}

 在定义上,我们知道,当我们将一个已经存在的对象用于创建另一个对象时,就会自动调用拷贝构造,那么问题来了,拷贝构造的实现原理就是:将传入的参数用于创建一个新的拷贝,那么,拷贝构造的机制就是用一个已经存在的对象创建另一个对象,这就会使拷贝构造函数调用自己进行拷贝,如此往复,无穷无尽。

         那么我们回到开始的问题,当我们使用拷贝构造的时候,我们如何解决类中的指针问题呢?

class  A
{
	int a;
	int* b;

public:
	void Print()
	{
		cout << a << endl;
	}
	~A()
	{
		free(b);
		cout << "~A" << endl;
	}
	A()
	{
		int a = 1;
		int* b = (int*)malloc(sizeof(int) * 1);
		*b = 1;
	}
	A(A& s)
	{
		a = s.a;
		b = (int*)malloc(sizeof(s.b));
		cout << sizeof(b)<< endl;
	}
};
 void Funk(A a)
 {
	 a.Print();
 }
	
int main()
{
	A s1;
	A s2(s1);
	return 0;
}

 说到拷贝,我们不就是将一个对象进行复制,创造一个一模一样的对象嘛,那么这里我们只需要将s1中b指针的大小计算出,然后在新拷贝的对象中开辟一个大小一样的空间不就行了吗?

二、运算符重载

我们写代码的时候经常使用一些运算符来对数据进行处理,例如:

那么对于自定义类型的数据,这些运算符还能够正常使用吗?

int main()
{
	A s1;
	A s2;
	int a = 1;
	int b = 2;
	a + b;
	a - b;
	s1 + s2;
	s1 - s2;
	return 0;
}

 

 答案是:不能,因为编译器根本不知道你要对这些自定义类型进行什么操作,编译器只知道内置类型要怎么操作。

那么,我们要怎样才能正常操作呢?

这里就用到了我们的运算符重载。

那么什么是运算符重载呢?

1.什么是运算符重载

运算符重载的意思和函数重载的意思没有什么区别,都是赋予其更多的选择,增加其匹配的参数,使其功能更多,但是二者并没有什么联系。

2.运算符重载的使用

运算符重载要用到一个新的东西,operator,它的后面跟需要重载的运算符,

语法为:  返回值    operator    运算符(参数){   函数体   }

class  A
{
	int a;
	

public:


	int operator+(A b)
	{
		return (a + b.a);

	}
};

 运算符重载常用于自定义类型的运算,赋予运算符更多的匹配参数有助于我们更加灵活的操作数据,但是有些运算符并不能进行运算符重载,如.、.*、::、?:、sizeof等,这些并不能进行运算符重载,大家一定要注意!


总结

运算符重载能够让我们更加了函数重载的意义,并且能够让我们更加灵活的处理数据,但是有少部分运算符并不能进行重载,这里需要注意,

文章中如有什么错误,望指正。————谢谢!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值