C++中拷贝构造函数

1.什么是拷贝构造函数:

拷贝构造函数嘛,当然就是拷贝和构造了。(其实很多名字,只要静下心来想一想,就真的是顾名思义呀)拷贝又称复制,因此拷贝构造函数又称复制构造函数。百度百科上是这样说的:拷贝构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。其唯一的参数(对象的引用)是不可变的(const类型)。此函数经常用在函数调用时用户定义类型的值传递及返回

 

2.拷贝构造函数的形式

Class X

{

public:

  X();

  X(const X&);//拷贝构造函数

}

2.1为什么拷贝构造参数是引用类型?

其原因如下:当一个对象以传递值的方式传一个函数的时候,拷贝构造函数自动被调用来生成函数中的对象(符合拷贝构造函数调用的情况)。如果一个对象是被传入自己的拷贝构造函数,它的拷贝构造函数将会被调用来拷贝这个对象,这样复制才可以传入它自己的拷贝构造函数,这会导致无限循环直至栈溢出(Stack Overflow)。

 

3.拷贝构造函数调用的三种形式

<span style="line-height: 1.5; color: rgb(0, 0, 102); font-family: verdana, arial, helvetica, sans-serif; font-size: 13px; background-color: rgb(255, 255, 255);"></span><pre name="code" class="cpp">#include <iostream>
using namespace std;

class point
{
public:
	point(int xx=0,int yy=0) {x=xx;y=yy;}
	point(point &p);
	int getx()	{return x;}
	int gety()	{return y;}
	void setx(int xx)	{x=xx;}
	void sety(int yy)	{y=yy;}
private:
	int x,y;
};

point::point(point &p)
{
	x=p.x;
	y=p.y;
	cout<<"Copy is called"<<endl;
}

point fun(point p)
{
	point p1;
	p1.setx(p.getx()+2);
	p1.sety(p.gety()+2);
	return p1;				//调用拷贝构造函数
}

void main()
{
	point a(2,3);
	point b(a);			//调用拷贝构造函数
	point c;
	c=fun(a);			//调用拷贝构造函数
	cout<<b.getx()<<" "<<b.gety()<<endl;
	cout<<c.getx()<<" "<<c.gety()<<endl;
}


 
  
<span style="line-height: 1.5; color: rgb(0, 0, 102); font-family: verdana, arial, helvetica, sans-serif; font-size: 13px; background-color: rgb(255, 255, 255);">3.1.一个对象作为函数参数,以值传递的方式传入函数体;</span>

3.2.一个对象作为函数返回值,以值传递的方式从函数返回;

3.3.一个对象用于给另外一个对象进行初始化(常称为复制初始化)。

总结:当某对象是按值传递时(无论是作为函数参数,还是作为函数返回值),编译器都会先建立一个此对象的临时拷贝,而在建立该临时拷贝时就会调用类的拷贝构造函数

 

4.深拷贝和浅拷贝

如果在类中没有显式地声明一个拷贝构造函数,那么,编译器将会自动生成一个默认的拷贝构造函数,该构造函数完成对象之间的位拷贝。(位拷贝又称浅拷贝,后面将进行说明。)自定义拷贝构造函数是一种良好的编程风格,它可以阻止编译器形成默认的拷贝构造函数,提高源码效率。

在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。事实上这就要用到深拷贝了,要自定义拷贝构造函数。

深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。下面举个深拷贝的例子。

#include <iostream>
using namespace std;


class CA
{
public:
     CA(int b,char* cstr)
     {
          a=b;
          str=new char[b];
          strcpy(str,cstr);
     }
     CA(const CA& C)
     {
          a=C.a;
          str=new char[a]; //深拷贝
          if(str!=0)
               strcpy(str,C.str);
     }
<span style="white-space:pre">	</span> 
     void seta(const char s,int i)
     {
<span style="white-space:pre">		</span> str[i]=s;
     }


     void Show()
     {
          cout<<str<<endl;
     }
    
     ~CA()
     {
          delete str;
     }


     private:
    
          int a;
          char *str;


};


int main()


{


     CA A(10,"Hello!");


     CA B=A;
     A.Show();
     B.Show();
     A.seta('W',3);
     A.Show();
     B.Show();
     return 0;


}



图一为深拷贝结果, 图二为浅拷贝结果

浅拷贝资源后在释放资源的时候会产生资源归属不清的情况导致程序运行出错一定要注意类中是否存在指针成员。

 

 

5.拷贝构造函数与“=“赋值运算符

例如:

class CExample

{};

int main()

{

CExample e1 = new CExample;

CExample e2 = e1;//调用拷贝构造函数

CExample e3(e1);//调用拷贝构造函数

CExample e4;

e4 = e1;//调用=赋值运算符

}

通常的原则是:①对于凡是包含动态分配成员或包含指针成员的类都应该提供拷贝构造函数;②在提供拷贝构造函数的同时,还应该考虑重载"="赋值操作符号。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值