【C++】深拷贝和浅拷贝解析

前言:

    最近在做C++二级题的时候总是会遇到深拷贝,想想最先接触这个词的时候是在大话设计模式中,但是C++中的深拷贝和C#中的略有区别,今天先来介绍一下C++中的深拷贝吧!

   简单的说:深拷贝的时候,相同数据指针指向不同的内存地址

              浅拷贝的时候,指针指向相同的内存地址

  现在来深入探讨一下:

     浅拷贝就是对内存地址的复制,让目标对象指针和源对象指向同一片内存空间,浅拷贝只是对对象的简单拷贝,让几个对象共用一片内存,当内存销毁的时候,指向这片内存的几个指针需要重新定义才可以使用,要不然会成为野指针。

<span style="font-family:KaiTi_GB2312;font-size:18px;">#include <iostream>  
#include <cstring>  
using namespace std;  
class Test  
{  
private:  
    int a;  
    char *str;  
public:  
    Test(int b, char *s)  
    {  
        a=b;  
        strcpy(str,s);  //肇事地点,但不是祸端  
    }  
    Test(const Test& C)  
    {  
        a=C.a;  
        strcpy(str,C.str);  
    }  
    void show ()  
    {  
        cout<<a<<","<<str<<endl;  
    }  
};  
   
int main()  
{  
    Test a(100,"hello");  
    Test b(a);  
    a.show();  
    b.show();  
    return 0;  
}</span>


   从上面的程序我们可以知道,对象a的数据成员a获得实际参数100的值,而数据成员str,即指针,是个随机地址值(指针的值,非指针指向的值)!在程序中,试图通过strcpy(str,s);将形式参数s指向的字符串"hello",复制到str所指向的那个位置,而那个位置,其地址并不是经过系统分配来的,这是个危险的操作。在这里,str这样未经过分配的地址(指针),被称为“野指针”。

  那么如何解决这个问题呢?

就是在构造函数中,要为指针类型的成员,分配专门的空间。以这条规则构建的复制,称作为深复制!深拷贝是指拷贝对象的具体内容,而内存地址是自主分配的,拷贝结束之后,两个对象虽然存的值是相同的,但是内存地址不一样,两个对象也互不影响,互不干涉。

 把上面的程序中改写一下完成深复制

<span style="font-family:KaiTi_GB2312;font-size:18px;">#include<iostream>  
using namespace std;  
class A  
{  
private:  
    int *arrayAddr;//保存一个有len个整型元素的数组的首地址  
    int len;       //记录动态数组的长度  
public:  
    A(int *a, int n);  
    ~A();  
    int sum();  
};  
A::A(int *a, int n)  
{  
    len=n;  
    arrayAddr=new int[n];  //为指针数据成员分配空间,注意,没有上面例子中加1那回事  
    for(int i=0; i<n; i++)  //逐个地将a指向的值逐个地复制过来  
    {  
        arrayAddr[i]=a[i];  
    }  
}  
//析构函数的类外定义,释放指针型数据a所指向的空间  
A::~A()  
{  
    delete [] arrayAddr;  
}  
int A::sum()   //获得a指向的数组中下标为i的元素的值  
{  
    int s=0;  
    for(int i=0; i<len; i++)  //逐个地将a指向的值逐个地复制过来  
    {  
        s+=arrayAddr[i];  
    }  
    return s;  
}  
  
int main(){  
    int b[10]= {75, 99, 90, 93, 38, 15, 5, 7, 52, 4};  
    A r1(b,10);  
    cout<<"和:"<<r1.sum()<<endl;  
    int c[15] = {18,68,10,52,3,19,12,100,56,96,95,97,1,4,93};  
    A r2(c,15);  
    cout<<"和:"<<r2.sum()<<endl;  
    return 0;  
}  </span>

如果不想看上面的内容,可以看最后总结。很经典的啊。

(1)什么时候用到拷贝函数?
  a.一个对象以值传递的方式传入函数体; 
  b.一个对象以值传递的方式从函数返回;
  c.一个对象需要通过另外一个对象进行初始化。
如果在类中没有显式地声明一个拷贝构造函数,那么,编译器将会自动生成一个默认的拷贝构造函数,该构造函数完成对象之间的位拷贝
(2)什么叫深拷贝?什么是浅拷贝?两者异同?
深如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。 
(3)深拷贝好还是浅拷贝好?
    如果实行浅拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。


 小结

不怕不知道就怕不知道,虽然之前学过C#中的深拷贝,但是和C++中的还是有区别的,通过总结让知识更有力量。

你可能感兴趣的:(【C++】深拷贝和浅拷贝解析)