从一道面试题看深拷贝、浅拷贝构造函数问题 (经典)

请看下面的程序,说说会出现什么问题?

#include <iostream>
#include <cstdlib>
#include <vector>  
using   namespace   std;  

class   CDemo  
{  
public:  
    CDemo(char*str=NULL)
	{
        
		if (str==NULL)//当初始化串不存在的时候,为m_data申请一个空间存放'\0';  
		{  
			m_str=new char[1];  
			*m_str='\0';  
		}  
		else//当初始化串存在的时候,为m_data申请同样大小的空间存放该串;  
		{  
			int length=strlen(str);  
			m_str=new char[length+1];  
			if (m_str==NULL)  
			{//内存是否申请成功    
				cout<<"申请内存失败!"<<endl;    
				exit(1);    
			}  
			strcpy(m_str,str);  
		}  
	}  
    ~CDemo()   
    {   
        if(m_str)   delete[]   m_str;   
    }  
	void show();
	
private:
    char*   m_str;  
};  
void CDemo::show()
{
	cout<<m_str<<endl;
}
int   main(int   argc,   char**   argv)  
{  
    CDemo   d1="trend   micro";  
	
	d1.show();
    vector<CDemo>   *a1=new   vector<CDemo>();  
    
    a1->push_back(d1); 
    delete   a1; 
	
    return 0;
}
这个程序在退出时,会出问题,什么问题?重复delete同一片内存,程序崩溃。
我们把析构函数改为如下,可以更清楚的看到这一点:
    ~CDemo()  
    {  
        if(str)
        {
            static int i=0;
            cout<<"&CDemo"<<i++<<"="<< (int*) this<<",    str="<< (int *) str<<endl;
            delete[]   str;     
        }
    }; 
   
运行时我们发现打印如下信息:
&CDemo0=000309D8,       str=000307A8
&CDemo1=0013FF70,       str=000307A8
也就是说,发生了CDemo类的两次析构,两次析构str所指向的同一内存地址空间(两次str值相同=000307A8)。
为什么?
《程序员面试宝典》第二版,P99,有句解释“vector对象指针能够自动析构,所以不需要调用delete a1,否则会造成两次析构对象”

我切以为这句话说的有点不妥。任何对象如果是通过new操作符申请了空间,必须显示的调用delete来销毁这个对象。所以“delete   a1;  ”这条语句是没有错误的。
这句话“vector<CDemo>   *a1=new   vector<CDemo>();  ”定一个指针,指向 vector<CDemo>,病用new操作符进行了初始化, 我们必须在适当的时候释放a1所占的内存空间,所以“delete   a1;  ”这句话是没有错误的。另外,我们必须明白一点, 释放vector对象,vector所包含的元素也同时被释放。

那到底那里错误?

这句a1的声明和初始化语句“vector<CDemo>   *a1=new   vector<CDemo>();  ”说明a1所含元素是“CDemo”类型的,在执行“a1->push_back(d1);  ”这条语句时,会调用CDemo的拷贝构造函数,虽然CDemo类中没有定义拷贝构造函数,但是编译器会为CDemo类构建一个默认的拷贝构造函数(浅拷贝),这就好像任何对象如果没有定义构造函数,编译器会构建一个默认的构造函数一样。

正是这里出了问题。a1中的所有CDemo元素的str成员变量没有初始化,只有一个四字节(32位机)指针空间。
“a1->push_back(d1);”这句话执行完后,a1里的CDemo元素与d1是不同的对象,但是a1里的CDemo元素的str与d1.str指向的是同一块内存,这从后来的打印信息就可以看出来。


我们知道,局部变量,如“CDemo   d1;  ” 在main函数退出时,自动释放所占内存空间,
那么会自动调用CDemo的析构函数“~CDeme”,问题就出在这里。

前面的“delete   a1;”已经把 d1.str 释放了(因为a1里的CDemo元素的str与d1.str指向的是同一块内存),main函数退出时,又要释放已经释放掉的 d1.str 内存空间,所以程序最后崩溃。

解释清楚了。
这里最核心的问题归根结底就是浅拷贝和深拷贝的问题。如果CDemo类添加一个这样的拷贝构造函数就可以解决问题:
   
CDemo(const   CDemo   &cd) 
{ 
    this->m_str   =   new   char[strlen(cd.m_str)+1]; 
    if (m_str==NULL)  
    {//内存是否申请成功    
	cout<<"申请内存失败!"<<endl;    
	exit(1);    
    }    
    strcpy(m_str,cd.m_str); 
}
这就是深拷贝。

或者这样用:
    vector<CDemo*>   *a1=new   vector<CDemo*>(); 
    a1->push_back(&d1); 
那么在    “delete   a1;” a1释放,同时a1里面包含的元素(”CDemo*“类型,仍然是一个指针,4字节空间)。 




参考:http://blog.chinaunix.net/uid-20384806-id-1954279.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值