likes
comments
collection
share

c++:类和对象中:拷贝构造和赋值运算符重载详解

作者站长头像
站长
· 阅读数 11

c++:类和对象

构造函数和析构函数详解


前言

拷贝构造,顾名思义就是复制.我们把一个已经创建自定义对象初始化创建新对象. 赋值运算符重载是把一个对象赋值给另一个对象,二者都是已创建的.


一、拷贝构造

怎么写拷贝构造

#include<iostream>
using namespace std;
class Date
{
public:
	//构造函数,对自定义对象进行初始化
	Date(int year = 2004, int month = 4, int day = 30)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	//拷贝构造
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	friend ostream& operator<<(ostream & out, Date & d);
private:
	int _year;
	int _month;
	int _day;
};
//这个是<<的运算符重载
ostream& operator<<(ostream& out, Date& d)
{
	cout << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}

int main()
{
	Date d1(2024,3,13);
	Date d2 = d1;
	cout << "d1:" << d1;
	cout << "d2:" << d2;

	return 0;
}

c++:类和对象中:拷贝构造和赋值运算符重载详解

1.拷贝构造也是构造函数的一种,构造函数没有值.所以拷贝构造也没有返回值**

2.拷贝构造只有一个形参,正常这个形参是自定义类型对象的引用.

比如在Date这个类里面就是Date&. 为什么一定要是引用呢?我们都知道,函数的参数是一个形参,我们调用函数传过去的才是实参.而形参是实参的一个拷贝,我们要传值过去就必须创建一个形参. 我们传自定义类型Date的值时,因为要拷贝一个新对象,所以就要调用Date类型的拷贝构造.要是拷贝构造里面的参数也是Date的话,就会继续调用它的拷贝构造.如此形成无限递归. c++:类和对象中:拷贝构造和赋值运算符重载详解 传值调用拷贝构造编译器会自动报错

3. 如果我们没有显示写拷贝构造,编译器会自己生成.但是这种只是浅拷贝,在某些情况下面有危险

c++:类和对象中:拷贝构造和赋值运算符重载详解

c++:类和对象中:拷贝构造和赋值运算符重载详解

编译器默认的拷贝构造函数是按字节进行拷贝,在我们上面的日期类适用.因为我们没有动态开辟空间.

typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 10)
	{
		//动态开辟空间
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
			perror("malloc fail");
			return;
		}
		_size = 0;
		_capacity = capacity;
	}
	void Push(const DataType& data)
	{
		_array[_size] = data;
		_size++;
	}
	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	size_t _size;
	size_t _capacity;
};
int main()
{
	Stack s1;
	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);
	Stack s2(s1);
	return 0;
}

上面的代码在程序运行会崩溃 c++:类和对象中:拷贝构造和赋值运算符重载详解 这是为什么?编译器不是会按字节去对内置类型进行拷贝吗? c++:类和对象中:拷贝构造和赋值运算符重载详解 结果调试,发现问题出现在free上,free被调用了两次.同一块空间被删除了两次.这才是导致问题出现的关键 第一次free c++:类和对象中:拷贝构造和赋值运算符重载详解

第二次free c++:类和对象中:拷贝构造和赋值运算符重载详解 编译器默认写的是浅拷贝,也叫值拷贝.它只会按字节去拷贝.在我们动态开辟空间时,浅拷贝不会开辟空间.这样子会导致两个对象的_array指针指向同一块空间. 自定义类型结束后会调用它的析构函数.因为有两个对象,所以析构两次,free两次.

	Stack(Stack& st)
	{
		DataType* tmp = (DataType*)malloc(st._capacity * sizeof(DataType));
		if (nullptr == tmp)
		{
			perror("malloc申请空间失败");
			return;
		}
		memcpy(tmp, st._array, st._capacity* sizeof(DataType));
		_array = tmp;
		_size = st._size;
		_capacity = st._capacity;
	}

上面的类里面加上拷贝构造函数就不会报错,能够正常运行.这个也就是深拷贝.

二、赋值运算符重载


代码实现

`#include<iostream>
using namespace std;

class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	//构造函数,对自定义对象进行初始化
	Date(int year = 2004, int month = 4, int day = 30)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//友元的声明
	friend ostream& operator<<(ostream& out, Date& d);

	//拷贝构造
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	//赋值运算符重载
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
};
	////运算符重载
ostream& operator<<(ostream& out, Date& d)
{
	cout << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}

int main()
{
	Date d1(2024,3,28);
	Date d2;
	cout << "d1:" << d1;
	cout << "d2:" << d2;

	d2 = d1;
	cout << "d1:" << d1;
	cout << "d2:" << d2;
	return 0;
}`

赋值运算符重载和拷贝构造类似,本质还是对对象的拷贝.只不过拷贝构造是在对象初始化时运用的,赋值运算符是对已经存在的对象赋值 赋值运算符重载编译器默认生成的跟拷贝构造一样,都是浅拷贝 c++:类和对象中:拷贝构造和赋值运算符重载详解 书写格式

	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}

1.operator是对内置操作符进行重载,如operator+ 2.重载操作符必须有一个类型参数,最好用const修饰一下 , const Date& 3.形参总是比操作数少一(a=b有两个操作数),因为成员函数的第一个参数为this指针 4.. :: sizeof ?: . 注意以上5个运算符不能重载.面试/笔试可能会考*

为什么返回值是Date&呢? 大家可能对这个问题比较好奇. 首先,我们的语言支持连等,我们可以同时给a,b,c三个变量赋值.

int main()
{
	int a,b,c;
	a = b = c = 10;
	return 0;
}

当我们把上面赋值运算符重载的代码改成无返回值时,我们的连续赋值就会报错. 在底层实现d3=d2=d1时,会调用赋值运算符重载这个函数,因为赋值是从右往左过去,故将d1赋值给d2,如果没有返回值的话,就没有人来给d3赋值.程序就会崩溃.所以返回值必须是左操作数的引用.

	void operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
	
	}

c++:类和对象中:拷贝构造和赋值运算符重载详解

总结

拷贝构造和赋值运算符是同一类成员函数.不主动写编译器会默认生成.但是生成的这个只是浅拷贝,在日期类这种没有分配资源的类可以用 但是在有开辟空间的类上不适用,要自己写.