拷贝构造函数之值传递的无穷递归与深浅拷贝

追求适度,才能走向成功;人在顶峰,迈步就是下坡;身在低谷,抬足既是登高;弦,绷得太紧会断;人,思虑过度会疯;水至清无鱼,人至真无友,山至高无树;适度,不是中庸,而是一种明智的生活态度。

导读:本篇文章讲解 拷贝构造函数之值传递的无穷递归与深浅拷贝,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

作者:非妃是公主
专栏:《笔记》《C++》
个性签:顺境不惰,逆境不馁,以心制境,万事可成。——曾国藩

在这里插入图片描述

C++学到类时,有一个难点,就是对拷贝构造函数的理解。

为什么拷贝构造函数必须采用引用传参,否则会引发无穷递归呢?

这个问题其实很简单,但是很多参考书上说的不是很明确,其实就是,再复制对象时要分为两个步骤,

第一步:开辟一个临时空间;
第二步:由于临时空间是需要构造的,重新调用拷贝构造函数(无穷递归形成…)

如果我们采用引用传参的方式,那么,在执行第二步时,不会调用复制构造函数,而是直接以地址的形式存储参数对象。即参数对象和新构建的对象地址相同。

说到此,我们不得不联想到另一个难以理解的问题,即深浅拷贝问题?为什么浅拷贝就可能会引起系统的崩溃呢?

因为,拷贝构造函数一共分为两种,一种为编译器默认提供的,另一种时我们自己定义的,这种是要在堆区重新开辟空间的。

我们来看一下浅拷贝的代码:
下面展示 浅拷贝代码片

#include<iostream>
using namespace std;
class Fraction {
	int m_numerator;//分子
	int m_denominator;//分母
public:
	Fraction(int above = 0, int below = 1) :
		m_numerator(above), m_denominator(below) {}//构造函数
	~Fraction() {}
};

当我们使用系统自带的拷贝构造函数时,发生的就是浅拷贝,即将旧对象的数据成员,原封不动的复制到新的对象,这对除指针之外的对象来说没什么问题。

但是,对于指针成员,将复制指针本身的值,而不是指针所指向的对象的值,这显然不符合预期。

这就会引发,在析构时会析构两次,进而导致程序崩溃。

但是,当我们使用我们自己构建的拷贝构造函数时则不然,我们相当于重新开辟了一个空间,然后再构造对象的时候,将原对象的所有数据成员,通过值传递的形式,传递给构建的对象。

现在我们来看一下深拷贝构造函数的定义:
下面展示 深拷贝代码片

#include<iostream>
using namespace std;
class Fraction {
	int m_numerator;//分子
	int m_denominator;//分母
public:
	Fraction(int above = 0, int below = 1) :
		m_numerator(above), m_denominator(below) {}//构造函数
	Fraction(const Fraction &rhs) :m_numerator(rhs.m_numerator), 
		m_denominator(rhs.m_denominator) {}//复制构造函数(深拷贝)
	~Fraction() {}
};

这样,两个对象就拥有了两个地址,也就解决了析构时,如果同一空间被析构两次造成的程序崩溃的问题。

很显然,深拷贝更加的安全稳定,所以,我们在用到拷贝复制函数时最好自己去定义一个。

那么有一个问题,为什么系统提供的拷贝构造函数,不直接进行深拷贝呢?即直接通过参数对象的数据成员,创建新的对象。

答案很简单,因为编译器并不知道我们定义的数据成员内部结构(比如:一个动物类,身高是吗哪一部分,体型是哪一部分等等)是怎样的,所以只能把一套数据原封不动的拷贝过来,这个有些类似于未定义错误。

如有问题,欢迎交流指正,谢谢。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/130604.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!