作者:非妃是公主
专栏:《笔记》《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