C++类对象的复制-拷贝构造函数(深拷贝浅拷贝)

2016-02-19 12:21 6 1 收藏

今天天气好晴朗处处好风光,好天气好开始,图老师又来和大家分享啦。下面给大家推荐C++类对象的复制-拷贝构造函数(深拷贝浅拷贝),希望大家看完后也有个好心情,快快行动吧!

【 tulaoshi.com - 编程语言 】

C++类对象的复制-拷贝构造函数(深拷贝,浅拷贝),进一步理解类成员的操作!

在学习这一章内容前我们已经学习过了类的构造函数和析构函数的相关知识,对于普通类型的对象来说,他们之间的复制是很简单的,例如:

int a = 10;
int b =a;

自己定义的类的对象同样是对象,谁也不能阻止我们用以下的方式进行复制,例如:


//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者

#include iostream
usingnamespacestd;

classTest
{
public:
Test(inttemp)
{
p1=temp;
}
protected:
intp1;

};

voidmain()
{
Test a(99);
Test b=a;
}


普通对象和类对象同为对象,他们之间的特性有相似之处也有不同之处,类对象内部存在成员变量,而普通对象是没有的,当同样的复制方法发生在不同的对象上的时候,那么系统对他们进行的操作也是不一样的,就类对象而言,相同类型的类对象是通过拷贝构造函数来完成整个复制过程的,在上面的代码中,我们并没有看到拷贝构造函数,同样完成了复制工作,这又是为什么呢?因为当一个类没有自定义的拷贝构造函数的时候系统会自动提供一个默认的拷贝构造函数,来完成复制工作。

下面,我们为了说明情况,就普通情况而言(以上面的代码为例),我们来自己定义一个与系统默认拷贝构造函数一样的拷贝构造函数,看看它的内部是如何工作的!

代码如下:


//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者

#include iostream
usingnamespacestd;

classTest
{
public:
Test(inttemp)
{
p1=temp;
}
Test(Test &c_t)//这里就是自定义的拷贝构造函数
{
cout"进入copy构造函数"p1=c_t.p1;//这句如果去掉就不能完成复制工作了,此句复制过程的核心语句
}
public:
intp1;
};

voidmain()
{
Test a(99);
Test b=a;
coutcin.get();
}

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)



上面代码中的Test(Test &c_t)就是我们自定义的拷贝构造函数,拷贝构造函数的名称必须与类名称一致,函数的形式参数是本类型的一个引用变量,且必须是引用

当用一个已经初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用,如果你没有自定义拷贝构造函数的时候系统将会提供给一个默认的拷贝构造函数来完成这个过程,上面代码的复制核心语句就是通过Test(Test &c_t)拷贝构造函数内的p1=c_t.p1;语句完成的。如果取掉这句代码,那么b对象的p1属性将得到一个未知的随机值;

下面我们来讨论一下关于浅拷贝和深拷贝的问题。

就上面的代码情况而言,很多人会问到,既然系统会自动提供一个默认的拷贝构造函数来处理复制,那么我们没有意义要去自定义拷贝构造函数呀,对,就普通情况而言这的确是没有必要的,但在某写状况下,类体内的成员是需要开辟动态开辟堆内存的,如果我们不自定义拷贝构造函数而让系统自己处理,那么就会导致堆内存的所属权产生混乱,试想一下,已经开辟的一端堆地址原来是属于对象a的,由于复制过程发生,b对象取得是a已经开辟的堆地址,一旦程序产生析构,释放堆的时候,计算机是不可能清楚这段地址是真正属于谁的,当连续发生两次析构的时候就出现了运行错误。

为了更详细的说明问题,请看如下的代码。

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)


//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者

#include iostream
usingnamespacestd;

classInternet
{
public:
Internet(char*name,char*address)
{
cout"载入构造函数"strcpy(Internet::name,name);
strcpy(Internet::address,address);
cname=newchar[strlen(name)+1];
if(cname!=NULL)
{
strcpy(Internet::cname,name);
}
}
Internet(Internet &temp)
{
cout"载入COPY构造函数"strcpy(Internet::name,temp.name);
strcpy(Internet::address,temp.address);
cname=newchar[strlen(name)+1];//这里注意,深拷贝的体现!
if(cname!=NULL)
{
strcpy(Internet::cname,name);
}
}
~Internet()
{
cout"载入析构函数!";
delete[] cname;
cin.get();
}
voidshow();
protected:
charname[20];
charaddress[30];
char*cname;
};
voidInternet::show()
{
coutvoidtest(Internet ts)
{
cout"载入test函数"}
voidmain()
{
Internet a("中国软件开发实验室","www.cndev-lab.com");
Internet b =a;
b.show();
test(b);
}



上面代码就演示了深拷贝的问题,对对象b的cname属性采取了新开辟内存的方式避免了内存归属不清所导致析构释放空间时候的错误,最后我必须提一下,对于上面的程序我的解释并不多,就是希望读者本身运行程序观察变化,进而深刻理解。

深拷贝和浅拷贝的定义可以简单理解成:如果一个类拥有资源(堆,或者是其它系统资源),当这个类的对象发生复制过程的时候,这个过程就可以叫做深拷贝,反之对象存在资源但复制过程并未复制资源的情况视为浅拷贝


浅拷贝资源后在释放资源的时候会产生资源归属不清的情况导致程序运行出错,这点尤其需要注意!

以前我们的教程中讨论过函数返回对象产生临时变量的问题,接下来我们来看一下在函数中返回自定义类型对象是否也遵循此规则产生临时对象!

先运行下列代码:



//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者

#include iostream
usingnamespacestd;

classInternet
{
public:
Internet()
{

};
Internet(char*name,char*address)
{
cout"载入构造函数"strcpy(Internet::name,name);
strcpy(Internet::address,address);
}
Internet(Internet &temp)
{
cout"载入COPY构造函数"strcpy(Internet::name,temp.name);
strcpy(Internet::address,temp.address);
cin.get

来源:http://www.tulaoshi.com/n/20160219/1600689.html

延伸阅读
极限复制给你拷贝装个加速器   ExtremeCopy(极限复制)是一款针对上述问题而研发的一款非常实用的高效文件复制移动加速软件。ExtremeCopy(极限复制)从Windows XP一路支持到Windows 8系统,通杀32和64位;可以非常有效的提高文件复制和移动的速度与稳定性。根据官方的说法其比系统自带的复制功能能提高20%-120%的复制移动速度。对于大...
标签: 电脑入门
相信大家都有这样的经历,使用Windows系统本身提供的文件复制和移动功能,虽然能够满足一般情况下的操作,但在拷贝文件量较大时,经常会出现力不从心的感觉,常常要占用很长时间才可结束。那么有没有什么工具可以加速拷贝呢? 本期笔者就为大家推荐一款国外开发的文件复制加速软件ExtremeCopy,该软件无论在WindowsXP,还是在Windows7都能获...
请注重,这一节内容是c++的重点,要非凡注重! 我们先说一下什么是构造函数? !-- frame contents -- !-- /frame contents -- 上一个教程我们简单说了关于类的一些基本内容,对于类对象成员的初始化我们始终是建立成员函数然后手工调用该函数对成员进行赋值的,那么在c++中对于类来说有没有更方便的方式能...
请注重,这一节内容是c++的重点,要非凡注重! 我们先说一下什么是构造函数。 !-- frame contents -- !-- /frame contents -- 上一个教程我们简单说了关于类的一些基本内容,对于类对象成员的初始化我们始终是建立成员函数然后手工调用该函数对成员进行赋值的,那么在c++中对于类来说有没有更方便的方式能...
     字符界面: 不管是Slackware 还是RedHat 安装后每次启 动时都会自动运行一个叫gpm的程序, 该程序运行后就可以用 鼠标来拷贝与粘贴了. 具体做法是按住鼠标左键拖动使要拷贝的地方反白, 这时反白的区域已经被拷贝, 再按鼠标右键拷贝的内容就会被粘贴在光标所在位置了.&nbs...