c++之拷贝构造的深浅拷贝问题

一、定义变量

内置类型定义变量

(1)int a=10;int b=10;
(2)int a=10,b=a;
我们要讨论的就是第二种定义方式,在自定义数据类型中,我们把这种定义方式叫做拷贝构造。

自定义数据类型定义变量

详细定义方式我们就不展开讲了,可以查看我相关文章。主要是就以下情况来介绍。
我么定义以下类,作为演示的自定义数据类型。
class person{
public:
	person (){}
	~person(){}
private:
	string name;
	int age;
};
类比int a=10,b=a;,我们可以定义person  p1;perosn p2=p1;在自定义数据类型中我多提一句
person p1;person p2(p1)  
和 person p1;perosn p2=p1;  
和person  p1, person p2=person p1;
和person p1,p2=p1;四者是等价的。
都是调用的拷贝构造。

拷贝构造内部实现(浅拷贝)

对于拷贝构造,就算你的类中没有拷贝构造,编译器也会提供一个拷贝构造来完成变量的定义。如下图:
class person{
public:
    //默认构造 编译器自动提供默认构造
    person (){
        cout <<"默认构造"<<endl;
    }
    //有参构造  编译器不会提供 当用户提供有参构造函数 编译器不再提供默认构造
    person(string name, int age){
        this->age=age;
        this->name=name;
        cout <<"有参构造"<<endl;
    }
    //拷贝构造 用户不提供拷贝构造 编译器会默认提供浅拷贝
    person(person &p){
        //系统提供的浅拷贝
            this->age=p.age;
            this->name=p.name;
    }
    void showinfo(){
        cout<<"年龄:"<<this->age<<"姓名:"<<this->name<<endl;
    }
    //析构函数
    ~person(){
    }
private:
    string name;
    int age;
};

深拷贝剖析

当类成员属性有一个为指针变量时,系统拷贝构造只会浅拷贝。
class person{
public:
    //默认构造 编译器自动提供默认构造
    person (){
        id=new int (-1);
        cout <<"默认构造"<<endl;
    }
    //有参构造  编译器不会提供 当用户提供有参构造函数 编译器不再提供默认构造
    person(string name, int age,int id){
        this->age=age;
        this->name=name;
        this->id=new int(id);
        cout <<"有参构造"<<endl;
    }
    //拷贝构造 用户不提供拷贝构造 编译器会默认提供浅拷贝 
    person(person &p){
        //系统提供的浅拷贝
            this->age=p.age;
            this->name=p.name;
        this->id=p.id;
    }
    void showinfo(){
        cout<<"年龄:"<<this->age<<"姓名:"<<this->name<<"ID号:"<<this->id<<endl;
    }
    //析构函数
    ~person(){
        if(this->id!=nullptr){
            delete this->id;
            this->id=nullptr;
        }
    }
private:
    string name;
    int age;
    int * id;
};
编译器所提供的默认拷贝构造是一个浅拷贝,所以带来的问题:当有属性指针指向堆区情况时,同一块空间将会被多次释放。就会造成程序的异常中止。

在这里插入图片描述

解决办法(整体源代码)

#include <iostream>

using namespace std;
class person{
public:
    //默认构造 编译器自动提供默认构造
    person (){
        id=new int (-1);
        cout <<"默认构造"<<endl;
    }
    //有参构造  编译器不会提供 当用户提供有参构造函数 编译器不再提供默认构造
    person(string name, int age,int id){
        this->age=age;
        this->name=name;
        this->id=new int(id);
        cout <<"有参构造"<<endl;
    }
    //拷贝构造 用户不提供拷贝构造 编译器会默认提供浅拷贝
    person(person &p){
        //系统提供的浅拷贝
            this->age=p.age;
            this->name=p.name;
        //this->i=p.i;
        //当有一变量为指针变量 在堆区开辟空间,使用上述浅拷贝就会造成两个指针变量指向同一堆区空间
        //在类对象被摧毁时会调用析构,而调用析构时必须把本类属性在堆区开辟的空间释放。如果使用浅拷贝。
        //一共创建两个对象,摧毁第一个对象是正常调用析构函数,当摧毁第二个对象时,它成员指针属性指向
        //的内存已经被上一个对象在调用析构函数时释放掉了。再释放就是非法访问会造成程序异常终止。
        //不懂可以看下图

        //自己需要深拷贝解决浅拷贝问题
        this->id=new int(*p.id);//再申请一个空间
    }
    void showinfo(){
        cout<<"年龄:"<<this->age<<"姓名:"<<this->name<<"ID号:"<<this->id<<endl;
    }
    //析构函数
    ~person(){
        if(this->id!=nullptr){
            delete this->id;
            this->id=nullptr;
        }
    }
private:
    string name;
    int age;
    int * id;
};
int main()
{
    person p1("张三",16,1008);
    person p2(p1);
    p1.showinfo();
    p2.showinfo();
    return 0;
}

拷贝构造的调用时机

1.用另外一个对象为本(正在生成的)对象进行初始化时,自动调用拷贝构造。(定义变量时)
2.当函数参数为类类型时,将自动调用拷贝构造。(内置数据类型值传递给函数传参)
3.当函数返回值为类类型时,将自动调用拷贝构造。(内置数据类型值函数值方式返回)

所以说:为了避免无意义拷贝过程,在函数传参时,或函数返回时,推荐使用引用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值