深拷贝与浅拷贝

来源:互联网 发布:金域名苑 编辑:程序博客网 时间:2024/06/05 05:45

默认情况下,c++编译器至少为我们写的类增加3个函数
1.默认构造函数(无参,函数体为空)
2.默认析构函数(无参,函数体为空)
3.默认拷贝构造函数,对类中非静态成员属性简单值拷贝
如果用户定义拷贝构造函数,c++不会再提供任何默认构造函数
如果用户定义了普通构造(非拷贝)函数,则c++编译器不在为我们提供默认的无参构造函数,但是会提供默认拷贝构造;

在同类中的两个对象之间可以赋值,这样使得两个对象的值相同,但是这两个对象仍然是两个不同的对象;

下面来个示例代码说明类中的浅拷贝问题:

#include<stdlib.h>#include <string>#include <iostream>using namespace std;class People{public:    People(string name,int age):m_Pname(name),m_age(age){cout<<"构造函数执行"<<endl;}    People(){}    void display()    {        cout<<"m_Pname = "<<m_Pname<<endl;        cout<<"m_age = "<<m_age<<endl;    }    ~People(){cout<<"析构函数执行"<<endl;}private:    string m_Pname;    int m_age;};void myFunc(){    People p1("lixiaogang",23);    People p2 = p1;    /* People p2 = p1;该语句会发生一个拷贝构造函数调用;    注意:People p2 = p1;不同于:People   p1("lixiaogang",23);People p2;p2 = p1;这里发生的操作是“赋值操作”,即直接将对象p1的值赋值给p2对象    */    p1.display();    puts("----------------");    p2.display();}int main(){    myFunc();    system("pause");    return 0;}

代码执行后结果为:

构造函数执行m_Pname = lixiaogangm_age = 23-----------------m_Pname = lixiaogangm_age = 23析构函数执行析构函数执行

这里的拷贝构造函数执行后没有任何的bug出现,现在我们把m_Pname 改为一个指向char 型的指针: char *pName之后,再来调试,代码如下:

#include<stdlib.h>#include <string.h>#include <iostream>using namespace std;class People{public:    People(const char *name,int age)    {        cout<<"构造函数执行"<<endl;        int len = strlen(name);        this->m_Pname = new char[ len + 1];        strcpy(this->m_Pname,name);        this->m_age = age;    }    People(){}    void display()    {        cout<<"m_Pname = "<<m_Pname<<endl;        cout<<"m_age = "<<m_age<<endl;    }    ~People()    {        cout<<"析构函数执行"<<endl;        if (this->m_Pname != NULL )        {            delete[] m_Pname;        }    }private:    char *m_Pname;    int m_age;};void myFunc(){    People p1("lixiaogang",23);    People p2 = p1;    p1.display();    puts("----------------");    p2.display();}int main(){    myFunc();    system("pause");    return 0;}

再来运行该代码,便会出现bug,程序直接死掉;如图:
这里写图片描述

分析原因在于程序中m_Pname指针在堆区中申请了一段内存空间,执行People p2 = p1语句时,把p1所指向的内存地址也给了p2一份,于是p2、p1都指向了同一个空间中,默认拷贝构造函数执行完成后,不会又问题,问题出现的是在析构函数阶段,从上面的代码段中可以看到“析构函数”被执行了两次,即就是p2,p1分别执行了一次析构;因此析构函数执行的时候,同一段内存空间被释放了两次,所以出现了错误(一个内存被释放之后, 该指针就不再执行这段内存空间了,如果此时再来对这个内存空间进行析构操作,这是属于非法的操作); 原理图如下:
这里写图片描述

解决方案是“自己重新定义一个拷贝构造函数,为新的对象p2分配一个新的内存空间,这样两个对象所指向的内存空间不是同一块地址,因此执行析构函数的时候也就不会bug了。

代码如下:

#include<stdlib.h>#include <string.h>#include <iostream>using namespace std;class People{public:    People(const char *name,int age)    {        cout<<"构造函数执行"<<endl;        int len = strlen(name);        this->m_Pname = new char[ len + 1];        strcpy(this->m_Pname,name);        this->m_age = age;    }    People(){}    /**** 拷贝构造函数 ****/    People(const People &p)    {        this->m_Pname = new char[strlen(p.m_Pname)+1];        strcpy(this->m_Pname,p.m_Pname);        this->m_age = p.m_age;    }    void display()    {        cout<<"m_Pname = "<<m_Pname<<endl;        cout<<"m_age = "<<m_age<<endl;    }    ~People()    {        cout<<"析构函数执行"<<endl;        if (this->m_Pname != NULL )        {            delete[] m_Pname;        }    }private:    char *m_Pname;    int m_age;};void myFunc(){    People p1("lixiaogang",23);    People p2 = p1;    p1.display();    puts("----------------");    p2.display();}int main(){    myFunc();    system("pause");    return 0;}

再次来运行程序,则完美运行,不会出现bug;其原理图如下:
这里写图片描述

结论:当类中有成员变量是指针时候,需要我们去为其分配内存空间,因此一旦类中有成员变量是指针的时候,往往需要我们自己提供几个函数,分别是拷贝构造函数、析构函数、构造函数、赋值函数。

0 0