C++拷贝构造函数

摘要:

1.代码格式牢记。类名(const 类名 &X)

2.该函数的三种调用时机。

3.浅拷贝与深拷贝(是否有动态成员变量)。

一、什么是拷贝构造函数

例1:

#include <iostream>  
using namespace std;  

class CExample {  
private:  
	int a;  
public:  
	//构造函数  
	CExample(int b)  
	{ a = b;}  

	//拷贝构造函数  
	CExample(const CExample& C)  
	{  
		a = C.a;  
	}  

	//一般函数  
	void Show ()  
	{  
		cout<<a<<endl;  
	}  
};  

int main()  
{  
	CExample A(100);  
	CExample B = A; // CExample B(A); 也是一样的  
	B.Show ();  
	return 0;  
}   
系统为对象B分配了内存并完成了与对象A的复制过程。就类对象而言, 相同类型的类对象是通过拷贝构造函数来完成整个复制过程的。CExample(const CExample& C)就是我们自定义的拷贝构造函数。可见,拷贝构造函数是一种特殊的构造函数,它 必须的一个参数是本类型的一个引用变量

二、拷贝构造函数的调用时机

在C++中,下面三种对象需要调用拷贝构造函数!

1.对象以值传递的方式传入函数参数

例2:

#include <iostream>
using namespace std;

class CExample   
{  
private:  
	int a;  

public:  
	//构造函数  
	CExample(int b)  
	{   
		a = b;  
		cout<<"creat: "<<a<<endl;  
	}  

	//拷贝构造  
	CExample(const CExample& C)  
	{  
		a = C.a;  
		cout<<"copy"<<endl;  
	}  

	//析构函数  
	~CExample()  
	{  
		cout<< "delete: "<<a<<endl;  
	}  

	void Show ()  
	{  
		cout<<a<<endl;  
	}  
};  

//全局函数,传入的是对象  
void g_Fun(CExample C)  
{  
	cout<<"test"<<endl;  
}  

int main()  
{  
	CExample test(1);  
	//传入对象  
	g_Fun(test);  
	return 0;  
}  
g_Fun(test) 执行步骤

①test对象传入形参时,会产生一个临时变量,设为C。

②然后调用拷贝构造函数把test的值给C。

③等g_Fun()执行完成后,析构C对象。

2.对象以值传递的方式从函数返回

例3:

#include <iostream>
using namespace std;

class CExample   
{  
private:  
	int a;  

public:  
	//构造函数  
	CExample(int b)  
	{   
		a = b;  
	}  

	//拷贝构造  
	CExample(const CExample& C)  
	{  
		a = C.a;  
		cout<<"copy"<<endl;  
	}  

	void Show ()  
	{  
		cout<<a<<endl;  
	}  
};  

//全局函数  
CExample g_Fun()  
{  
	CExample temp(0);  
	return temp;  
}  

int main()  
{  
	g_Fun();  
	return 0;  
}  
当g_Fun()函数执行到return时,会产生以下几个重要步骤:

①先会产生一个临时变量,就叫XXXX吧。

②然后调用拷贝构造函数把temp的值给XXXX。整个这两个步骤有点像:CExample XXXX(temp);

③在函数执行到最后先析构temp局部变量。(析构顺序

④等g_Fun()执行完后再析构掉XXXX对象。

3.对象需要另外一个对象初始化

CExample A(100);  
CExample B = A; 
最后一句会调用拷贝构造函数。
若程序如下,则不会调用拷贝构造函数。即:CExample B = A;与B = A;的区别。

CExample a(1);
CExample b(2);
b = a;
最后一句为调用拷贝构造函数,调用了等号运算符重载。程序代码示例如下(注意其中this与*this区别)

CExample &operator=( const CExample &rhs )
{
	if ( this == &rhs ) {
            return *this;
        }
        this->a = rhs.a;
        return *this;
}

三、浅拷贝与深拷贝

系统自带默认的拷贝构造函数。

1.浅拷贝

只对对象中的数据成员进行简单的赋值。大多数情况下“浅拷贝”已经能很好地工作了,但是一旦对象存在了动态成员,那么拷贝就会出问题了。如下代码:

    class Rect  
    {  
    public:  
        Rect()      // 构造函数,p指向堆中分配的一空间  
        {  
            p = new int(100);  
        }  
        ~Rect()     // 析构函数,释放动态分配的空间  
        {  
            if(p != NULL)  
            {  
                delete p;  
            }  
        }  
    private:  
        int width;  
        int height;  
        int *p;     // 一指针成员  
    };  
      
    int main()  
    {  
        Rect rect1;  
        Rect rect2(rect1);   // 复制对象  
        return 0;  
    }  
error:进行对象复制时,对于动态分配的内容没有进行正确的操作。

在运行定义rect1对象后,由于在构造函数中有一个动态分配的语句,因此执行后的内存情况大致如下:

在使用rect1复制rect2时,由于执行的是拷贝,只是将成员的值进行赋值,这时rect1.p=rect2.p,也即这两个指针指向了堆里的同一个空间,如下如所示:

当然,这不是我们所期望的结果,在销毁对象时,两个对象的析构函数将对同一个内存空间释放两次,这就是错误出现的原因。我们需要的不是两个p有相同的值,而是两个p指向的空间有相同的值。解决办法就是使用“深拷贝”。

2.深拷贝

在“深拷贝”的情况下,对于对象中动态成员,应该重新动态分配空间,如下所示。

    class Rect  
    {  
    public:  
        Rect()      // 构造函数,p指向堆中分配的一空间  
        {  
            p = new int(100);  
        }  
        Rect(const Rect& r)  
        {  
            width = r.width;  
            height = r.height;  
            p = new int;    // 为新对象重新动态分配空间  
            *p = *(r.p);  
        }  
        ~Rect()     // 析构函数,释放动态分配的空间  
        {  
            if(p != NULL)  
            {  
                delete p;  
            }  
        }  
    private:  
        int width;  
        int height;  
        int *p;     // 一指针成员  
    };  
完成对象复制后,内存情况如下所示:

此时rect1的p和rect2的p各自指向一段内存空间,但它们指向的空间具有相同的内容,这就是所谓的“深拷贝”。


参考:

http://blog.csdn.net/lwbeyond/article/details/6202256

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值