C++ 深拷贝浅拷贝

一、概念

深拷贝:指的是拷贝一个对象时,不仅仅把对象的引用进行复制,还把该对象引用的值也一起拷贝。这样进行深拷贝后的拷贝对象就和源对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。

浅拷贝:指的是拷贝一个对象时,仅仅拷贝对象的引用进行拷贝,但是拷贝对象和源对象还是引用同一份实体。此时,其中一个对象的改变都会影响到另一个对象。

浅拷贝示例:

#include<iostream>
using namespace std;

/*
深拷贝浅拷贝
1、定义一个Array类,数据成员m_iCount,成员函数:构造、析构、拷贝构造、成员封装,通过此例体会浅拷贝
2、增加数据成员m_pArr,并增加m_pArr地址查看函数,同时改造构造函数、拷贝构造和析构,体会深拷贝原理和必要性
*/

class Array
{
public:
	Array();
	Array(const Array &arr);
	~Array();
	void setCount(int count);
	int getCount();
private:
	int m_iCount;
};

Array::Array()
{
	cout<<"Array()"<<endl;
}

Array::Array(const Array &arr)
{
	m_iCount = arr.m_iCount;//浅拷贝
	cout<<"Array()&"<<endl;
}


Array::~Array()
{
	cout<<"~Array()"<<endl;
}

void Array::setCount(int count)
{
	m_iCount = count;
}

int Array::getCount()
{
	return m_iCount;
}

int main()
{
	Array arr1;
	arr1.setCount(5);

	Array arr2(arr1);//用arr1实例化arr2 会调用arr1的拷贝构造函数
	cout<<"arr2.m_iCount值"<<arr2.getCount()<<endl;//5 用arr1实例化arr2的时候,就将arr1中的m_iCount赋值给了arr2中的m_iCount
	return 0;
}

运行结果:

用arr1实例化arr2的时候,就将arr1中的m_iCount赋值给了arr2中的m_iCount,这个代码中的拷贝构造函数中实现的是浅拷贝。那么浅拷贝会存在哪些问题呢?我们来看下一个代码

#include<iostream>
using namespace std;

/*
深拷贝浅拷贝
1、定义一个Array类,数据成员m_iCount,成员函数:构造、析构、拷贝构造、成员封装,通过此例体会浅拷贝
2、增加数据成员m_pArr,并增加m_pArr地址查看函数,同时改造构造函数、拷贝构造和析构,体会深拷贝原理和必要性
*/

class Array
{
public:
	Array(int count);
	Array(const Array &arr);
	~Array();
	void setCount(int count);
	int getCount();
	void printAddr();//地址查看函数
private:
	int m_iCount;
	int *m_pArr;
};

Array::Array(int count)
{
    m_iCount = count;
    m_pArr = new int[m_iCount];
    cout<<"Array()"<<endl;
}

Array::Array(const Array &arr)
{
    //浅拷贝是利用赋值的方式来拷贝的
    m_pArr = arr.m_pArr;//浅拷贝 两个指向了同一段内存
    m_iCount = arr.m_iCount;
    cout<<"Array()&"<<endl;
}


Array::~Array()
{
    delete []m_pArr;
    m_pArr = NULL;
    cout<<"~Array()"<<endl;
}

void Array::setCount(int count)
{
    m_iCount = count;
}

int Array::getCount()
{
    return m_iCount;
}
void Array::printAddr()
{
    cout<<"m_pArr的值是"<<m_pArr<<endl;
}


int main()
{
    Array arr1(5);
    arr1.printAddr();
    Array arr2(arr1);
    arr2.printAddr();

    return 0;
}

运行结果:

根据运行结果,我们发现通过arr1实例化出来的arr2的m_pArr的值与arr1相同,说明这两个指针指向了同一块内存,在调用析构函数的时候,arr1会释放一遍,arr2也会释放一遍,但是他们指向同一块内存,所以相当于让同一块内存释放两遍,会造成程序崩溃!!!

那么如何解决这样的问题呢?

利用深拷贝
 

#include<iostream>
using namespace std;

/*
深拷贝浅拷贝
1、定义一个Array类,数据成员m_iCount,成员函数:构造、析构、拷贝构造、成员封装,通过此例体会浅拷贝
2、增加数据成员m_pArr,并增加m_pArr地址查看函数,同时改造构造函数、拷贝构造和析构,体会深拷贝原理和必要性
*/

class Array
{
public:
    Array(int count);
    Array(const Array &arr);
    ~Array();
    void setCount(int count);
    int getCount();
    void printAddr();//地址查看函数
    void printArr();//打印数组中的元素
private:
    int m_iCount;
    int *m_pArr;
};

Array::Array(int count)
{
    m_iCount = count;
    m_pArr = new int[m_iCount];
    for(int i = 0;i < m_iCount;i++)
    {
        m_pArr[i] = i;
    }
    cout<<"Array()"<<endl;
}

Array::Array(const Array &arr)
{
    //深拷贝 
    m_iCount = arr.m_iCount;
    m_pArr = new int[m_iCount];//先分配一段内存
    for(int i = 0;i < m_iCount;i++)//将传入进来的对象对应的的数据拷贝到新申请的内存中
    {
	m_pArr[i] = arr.m_pArr[i];
    }
    cout<<"Array()&"<<endl;
}


Array::~Array()
{
    delete []m_pArr;
    m_pArr = NULL;
    cout<<"~Array()"<<endl;
}

void Array::setCount(int count)
{
    m_iCount = count;
}

int Array::getCount()
{
    return m_iCount;
}

void Array::printAddr()
{
    cout<<"m_pArr的值是"<<m_pArr<<endl;
}

void Array::printArr()
{
    for(int i = 0;i < m_iCount;i++)
    {
        cout<<m_pArr[i]<<endl;
    }
}

int main()
{
    Array arr1(5);
    arr1.printAddr();
    arr1.printArr();
    
    Array arr2(arr1);
    arr2.printAddr();
    arr2.printArr();

    return 0;
}

运行结果:

我们可以看arr1中和arr2中的两个指针指向了不同的内存,arr2成功地拷贝了arr1中的数据,并且执行了两次析构函数

总结:浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。

注意:

  • 对一个已知对象进行拷贝,编译系统会自动调用一种构造函数——拷贝构造函数,如果用户未定义拷贝构造函数,则会调用默认拷贝构造函数,是浅拷贝。
  • 在对含有指针成员的对象进行拷贝时,必须要自己定义拷贝构造函数,使拷贝后的对象指针成员有自己的内存空间,即进行深拷贝,这样就避免了内存泄漏发生。

学习参考:

C++面试题之浅拷贝和深拷贝的区别

一个完整的自定义类实现可参考:C++笔试题之String类的实现

 

 

 

 

 

 

设计并实现一个动态整型数组类Vect,要求: (1)实现构造函数重载,可以根据指定的元素个数动态创建初始值为0的整型数组,或根据指定的内置整型数组动态创建整型数组。 (2)设计拷贝构造函数和析构函数,注意使用深拷贝。 (3)设计存取指定位置的数组元素的公有成员函数,并进行下标越界,若越界则输出“out of boundary”。 (4)设计获取数组元素个数的公有成员函数。 (5)设计用于输出数组元素的公有成员函数,元素之间以空格分隔,最后以换行符结束。 在main函数中按以下顺序操作: (1)根据内置的静态整型数组{1,2,3,4,5}构造数组对象v1,根据输入的整型数构造数组对象v2。 (2)调用Vect的成员函数依次输出v1和v2的所有元素。 (3)输入指定的下标及对应的整型数,设置数组对象v1的指定元素。 (4)根据数组对象v1拷贝构造数组对象v3。 (5)调用Vect的成员函数依次输出v1和v3的所有元素。 设计并实现一个动态整型数组类Vect,要求: (1)实现构造函数重载,可以根据指定的元素个数动态创建初始值为0的整型数组,或根据指定的内置整型数组动态创建整型数组。 (2)设计拷贝构造函数和析构函数,注意使用深拷贝。 (3)设计存取指定位置的数组元素的公有成员函数,并进行下标越界,若越界则输出“out of boundary”。 (4)设计获取数组元素个数的公有成员函数。 (5)设计用于输出数组元素的公有成员函数,元素之间以空格分隔,最后以换行符结束。 在main函数中按以下顺序操作: (1)根据内置的静态整型数组{1,2,3,4,5}构造数组对象v1,根据输入的整型数构造数组对象v2。 (2)调用Vect的成员函数依次输出v1和v2的所有元素。 (3)输入指定的下标及对应的整型数,设置数组对象v1的指定元素。 (4)根据数组对象v1拷贝构造数组对象v3。 (5)调用Vect的成员函数依次输出v1和v3的所有元素。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值