(C++)拷贝构造

概念

拷贝,我们都知道

那么,对象拷贝,该如何拷贝呢?

像这样吗?

代码:

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;
	}
private:
	int _day;
	int _month;
	int _year;
};

int main()
{
	Date d1(2023, 7, 31);
	Date d2;
	d2 = d1;//将d1赋给d2
	d2.print();
	return 0;
}

 

我们看到,d1确实拷贝给了d2,但是对象之间的拷贝真的是这样吗?

拷贝构造函数又是怎么写的呢?

拷贝构造函数:

 这里 d 相当于要被拷贝的对象的别名,也就是下面d1的引用,然后我们将d 中的成员依次赋值给创建的对象。

 那为什么要用引用呢?

这样不可以吗?

 答:这样会发生无穷递归的问题

所以一定要用引用!!! 

我们说,拷贝构造函数时默认成员函数,那么若未显式定义,编译器会生成默认的拷贝构造函数。

这就是我们一开始为什么没写拷贝构造,d2仍然拷贝成功了的原因:

 注意:在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定 义类型是调用其拷贝构造函数完成拷贝的。

那,既然默认生成,我们还需要写吗?不麻烦吗?

答:当然要写,对于日期类来说,可以不写,默认生成的就可以了。但是对于一些需要开空间的,就需要自己写。

比如,像栈类这种的:

代码:

typedef int DataType;
class Stack
{
public:
//构造
	Stack(size_t capacity = 4)
	{
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			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;
}

像这种,没有写拷贝构造,用默认的就会崩溃!

崩溃在这:

 在调用析构函数时崩溃,为什么呢?

我们来看:

 两者指向同一块空间,那s1,s2在销毁时,都要调用析构函数,这块空间就相当于释放了两次,所以程序崩溃了!一块内存空间多次释放,导致程序崩溃。

这就需要写一个拷贝构造来解决这个问题

代码:

Stack(const Stack& st)
	{
		//新开一块一样大小的空间
		_array = (DataType*)malloc(st._capacity * sizeof(DataType));
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
        //拷贝里面的数据
		memcpy(_array, st._array, st._capacity * sizeof(DataType));

		_size = st._size;
		_capacity = st._capacity;

	}

注意:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请 时,则拷贝构造函数是一定要写的,否则就是浅拷贝。

总结

特征:

  1. 拷贝构造函数是构造函数的一个重载形式。
  2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用。
  3. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按 字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
  4. 一旦涉及到资源申请 时,则拷贝构造函数是一定要写的,否则就是浅拷贝。

拷贝构造函数典型调用场景:

  • 使用已存在对象创建新对象
  • 函数参数类型为类类型对象
  • 函数返回值类型为类类型对象

为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用 尽量使用引用。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值