c++拷贝函数——深浅拷贝

拷贝构造函数

  • 拷贝构造函数也是构造函数,长相和构造函数一样的,只是参数是固定

    • 拷贝构造函数唯一的参数是对对象引用

  • 不写拷贝构造函数,也存在一个默认的拷贝构造函数

  • 拷贝构造函数作用: 通过一个对象去初始化另一个对象

  • 思考问题

    • 什么时候调用拷贝构造?

      • 当通过一个对象去创建出来另一个新的对象时候需要调用拷贝

    • 拷贝构造什么时候需要加const修饰参数?

      • 当存在匿名对象赋值操作的时候,必须要const修饰

  • class MM
    {
    public:
    	MM() = default;
    	MM(string name, int age) :name(name), age(age) {}
    	void print()
    	{
    		cout << name << "\t" << age << endl;
    	}
    	//拷贝构造
    	MM(const MM& mm)			  //MM girl(mm);
    	{
    		name = mm.name;           //girl.name=mm.name
    		age = mm.age;	          //girl.age=mm.age
    		cout << "拷贝构造" << endl;
    	}
    
    
    protected:
    	string name;
    	int age;
    };
    int main()
    {
    	MM mm("mm", 18);
    	mm.print();
    	//显示调用调用
    	cout << "显示调用" << endl;
    	MM girl(mm);        //通过一个对象创建另一个对象
    	girl.print();
    	//隐式调用
    	cout << "隐式调用" << endl;
    	MM girl2 = mm;		//拷贝构造
    	girl2.print();
    
    	/* 打印结果
    	mm      18
        显示调用调用
        拷贝构造
        mm      18
        隐式调用
        拷贝构造
        mm      18*/
    	return 0;
    }
  • 上述的代码可以看到,通过一个对象创建另一个对象的方式有小括号赋值和等号运算符。 
  • 我们在还可以通过函数传参的方式来进行对象的拷贝。
  • class MM
    {
    public:
    	MM() = default;
    	MM(string name, int age) :name(name), age(age) {}
    	void print()
    	{
    		cout << name << "\t" << age << endl;
    	}
    	//拷贝构造
    	MM(const MM& mm)			 
    	{
    		name = mm.name;           
    		age = mm.age;	          
    		cout << "拷贝构造" << endl;
    	}
    
    
    protected:
    	string name;
    	int age;
    };
    
    void printData1(MM mm1)   /*MM mm=实参;这是又定义了一个类类型的mm1,
    						  再将main函数中的mm拷贝给mm1,相当于MM mm1(mm)*/
    {
    	mm1.print();
    }
    void printData2(MM& mm1)   /*不存在拷贝本 这是给main函数中的mm起别名,
    						   所以该函数的打印与直接打印main函数中的mm是一样的*/
    {
    	mm1.print();
    }
    
    int main()
    {
    	MM mm = { "小红",20 };
    	//函数传参
    	cout << "第一种调用形态" << endl;
    	printData1(mm);
    	cout << "第二种调用形态" << endl;
    	printData2(mm);   //由打印结果可以看出,运用函数传参的时候引用与不引用的区别  
    	/* 打印结果
    	第一种调用形态
        拷贝构造
        小红    20
        第二种调用形态
        小红    20*/
    	return 0;
    }

  •  还有一种对象——无名对象(匿名对象)
  • 匿名对象就是没有定义名字的对象,并且使用匿名对象的时候,拷贝函数的参数必须有const修饰
    //无名对象 匿名对象
    	MM temp;
    	temp = MM("匿名", 18);
    	temp.print();
    
    	//匿名对象创建对象时候,拷贝构造一定要用const修饰
    	MM temp2 = MM("匿名", 199);
    	temp2.print();
    
    	/* 打印结果
    	匿名    18
        匿名    199*/

    如果不加入const修饰:

     

    深浅拷贝

    浅拷贝: 默认的拷贝构造叫做浅拷贝

    深拷贝: 拷贝构造函数中做了new内存操作,并且做拷贝赋值的操作

  • 如果我们在使用了指针,并且new了一段内存,使用默认的拷贝函数时,就会发生问题

  •  
  • 这里可能会有一点搞不清楚为什么发生程序崩溃的错误,我们来画图描述一下。

 

  • 这样的拷贝,只是改变了mm1的name的指针方向,它与mm共同指向同一个内存,然后我们又知道,指针在最后是需要析构函数来释放的,那么mm的name指针指向的内容被释放了,mm1的name指针又该指向什么呢?
  • 所以这就是为什么在使用指针的时候,我们已经不能使用默认的拷贝函数,需要直接再写一个拷贝函数——也就是深拷贝 
    class MM
    {
    public:
    	MM(const char* mname, int age) :age(age)
    	{
    		name = new char[strlen(mname) + 1];
    		strcpy_s(name, strlen(mname) + 1, mname);
    	}
    	MM(const MM& object)   //这就是深拷贝函数(一定是引用)
    	{
    		//name = object.name;
    		name = new char[strlen(object.name) + 1];   //这里重新给需要拷贝的name申请了一段新的空间
    		strcpy_s(name, strlen(object.name) + 1, object.name);
    		//name = object.name;
    		age = object.age;
    	}
    
    	void print()
    	{
    		cout << name << "\t" << age << endl;
    	}
    	~MM()
    	{
    		delete[] name;
    		name = nullptr;
    	}
    protected:
    	char* name;
    	int age;
    };
    int main()
    {
    	{
    		MM mm = { " 小红",20 };
    		MM mm1 = mm;
    		mm.print();
    		mm1.print();
    	}
    
    	/* 打印结果
    	 小红   20
         小红   20*/
    	return 0;
    }
  • 使用深拷贝函数的时候,就可以重新给一段新的内存,这样两个对象的name指针都会指向不同的两个空间,但是他们的的内容是一样的,那么他们进行析构函数释放内存的时候就不会发生冲突。

我们总结一下这个深拷贝,当你在类中使用了指针并且申请内存的时候,如果这个时候你需要进行拷贝操作就必须要使用深拷贝,要不然程序在运行的时候会崩溃 

设计并实现一个动态整型数组类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的所有元素。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值