使用缺省的拷贝构造函数带来的危险性

原创 2012年02月19日 15:49:54

我此前另外一篇文章通过类String看拷贝构造函数,赋值函数的作用和区别

对于更深的拷贝构造函数讨论大家可以参见这篇帖子

C++类对象的复制-拷贝构造函数

通过编写类String的拷贝构造函数和赋值函数介绍了一些拷贝构造数.本文着重介绍拷贝构造函数的作用和重要性。

首先介绍下拷贝构造函数的使用范围即作用:

1) 一个对象以值传递的方式传入函数体;

2) 一个对象以值传递的方式从函数返回;
3) 一个对象需要通过另外一个对象进行初始化;

如果在前两种情况不使用拷贝构造函数的时候,就会导致一个指针指向已经被删除的内存空间。对于第三种情况来说,初始化和赋值的不同含义是构造函数调用的原因。事实上,拷贝构造函数是由普通构造函数和赋值操作符共同实现的

  拷贝构造函数不可以改变它所引用的对象,其原因如下:当一个对象以传递值的方式传一个函数的时候,拷贝构造函数自动的被调用来生成函数中的对象。如果一个对象是被传入自己的拷贝构造函数,它的拷贝构造函数将会被调用来拷贝这个对象,这样复制才可以传入它自己的拷贝构造函数,这会导致无限循环直至栈溢出(Stack Overflow)。除了当对象传入函数的时候被隐式调用以外,拷贝构造函数在对象被函数返回的时候也同样的被调用。


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

1. 不自行定义拷贝构造函数产生的问题:

class CA
{	
	char* p;
public:
	CA(const char* a)
	{
		if(NULL!=a)
		{
			p = new char[strlen(a)+1];
			strcpy(p,a);
		}
		else
		{
			p = new char[1];
			p='\0';
		}
	};

// 	 CA(CA& mself)
// 	{
// 		p=new char[strlen(mself.p)+1];
// 		strcpy(p,mself.p);
// 	}

	void Show()
	{
		printf("成员变量:%s\n",p);
	}
	~CA()
	{
		printf("析构函数\n");
		delete p;
	}
	
}; 


测试函数:
	CA ca("hello");
	CA cb= ca;
	cb.Show();

执行结果:


当我们不使用自定义拷贝构造函数时,在系统自动调用析构函数时,同一片内存区域p会被释放两次,这时候就会出现上图中的释放错误.而我们将代码中注释掉的自定义拷贝构造函数放开,则不会出现这样的错误。


2. 拷贝构造函数和构造函数,以及无名对象。

先声明定义一个类

class Internet  
{  
public:   
	Internet(char *name)  
	{  
		std::cout<<"载入构造函数"<<std::endl;  
		strcpy(Internet::name,name);  
	}  
	Internet(Internet &temp)  
	{  
		std::cout<<"载入COPY构造函数"<<std::endl;  
		strcpy(Internet::name,temp.name);  
		std::cin.get();  
	}  
	~Internet()  
	{  
		std::cout<<"载入析构函数!";  
		std::cin.get();  
	}  
	void Show()
	{
		std::cout<<name<<std::endl;
	}
protected:  
	char name[20];  
}; 
2.1 无名对象

无名对象可以作为实参传递给函数,可以拿来拷贝构造一个新对象,也可以初始化一个引用的声明。

2.1.1

	Internet b = Internet("test");// 初始化对象定义

图2.1

上面执行的是用无名对象拷贝构造一个对象b。按理说C++先调用构造函数Internet(char*);

创建一个无名对象,然后再调用拷贝构造函数Internet(Internet&);(或许是默认的)创建对象b;但是,由于是用无名对象去拷贝创建一个对象,拷贝完后,无名对象就失去了任何作用,对于这种情况,C++特别将其看作为Internet b = "test";效果一样,而且可以省略创建无名对象这一步。

如测试

Internet b = "test";

或者

Internet b("test");

输出的结果都是和上面一样的,因此可以把上面这3个初始化b的方式看成一样的。

2.1.2

Internet& refs = Internet("测试"); // 初始化引用

上面执行的是拿无名对象初始化一个引用。由于是在函数内部,所以无名对象作为局部对象产生在栈空间中,从作用域上看,该引用与无名对象是相同的,它完全等价于Internet refs = "测试";所以这种使用是多余的。


2.1.3

先定义一个使用自定义类型作参数的函数

void fn(Internet s)
{	
}
测试函数

fn(Internet("测试"));
输出结构仍然是图2.1

这次执行的是无名对象作为实参传递给形参s,C++先调用构造函数创建一个无名对象,然后该无名对象初始化了引用形参s对象,由于实参是在主函数中,所以无名对象是在主函数的栈区中创建,函数fn()的形参s引用的是主函数栈空间中的一个对象。它等价于:

Internet s("测试");
fn(s);
如果对象s仅仅是为了充当函数fn()实参的需要,完全可以用第

fn(Internet("测试"));
来代替。


默认构造函数和拷贝构造函数

如果类包含内置或复合类型的成员,则该类就不应该依赖于合成的默认构造函数,它应该定义自己的构造函数来初始化这些成员。...
  • Sun19910114
  • Sun19910114
  • 2014年10月08日 09:51
  • 1972

默认拷贝构造函数,浅拷贝,深拷贝

类定义中,如果未提供自己的拷贝构造函数,则C++提供一个默认拷贝构造函数,就像没有提供构造函数时,C++提供默认构造函数一样。   C++提供的默认拷贝构造函数工作的方法是:完成一个成员一个成员的拷贝...
  • zzwdkxx
  • zzwdkxx
  • 2016年06月27日 17:04
  • 1344

C++虚基类构造函数详解(调用顺序)之一

原文  http://www.cnblogs.com/haoyuanyuan/archive/2013/04/25/3041250.html 主题 C++ 面向对象编程 解释某个函...
  • SunboyJohn690905084
  • SunboyJohn690905084
  • 2016年07月17日 10:50
  • 1375

C++的缺省的拷贝构造函数和赋值运算符重载函数

C++的缺省的拷贝构造函数和赋值运算符重载函数
  • wu4long
  • wu4long
  • 2014年02月13日 18:23
  • 1079

smartptr应用及拷贝构造函数使用 源码

  • 2010年11月10日 16:05
  • 6KB
  • 下载

C++中拷贝构造函数的使用

  • 2012年07月29日 08:56
  • 275KB
  • 下载

拷贝构造函数

  • 2014年03月10日 22:22
  • 35KB
  • 下载

C++拷贝构造函数和赋值操作

  • 2012年01月10日 13:51
  • 3KB
  • 下载

拷贝构造函数..........

  • 2008年09月11日 23:26
  • 3KB
  • 下载

Day39、构造函数重载、缺省构造函数、拷贝构造函数、初始化表

一、            构造函数和初始化表 1、构造函数可以重载 1)构造函数通过参数表的差别化形成重载关系,创建对象时通过构造实参类型选择匹配,表示不同的对象创建方式 2)可以适当使用缺省...
  • tangzihao_c
  • tangzihao_c
  • 2016年09月24日 10:00
  • 145
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:使用缺省的拷贝构造函数带来的危险性
举报原因:
原因补充:

(最多只允许输入30个字)